


;         	PROGRAM DXED.COM - DX7 VOICE EDITOR AND LIBRARIAN

;		Debugged to 16 August 2000

          	DATA SEGMENT

          	ORG 07000H

          	OUT_BUF DB 4010 DUP (48)   	;data entry screen buffer
          	BUF DB 5000 DUP (48)      	;main voice buffer
		DXEXIM_BUF DB 5000 DUP (0)	;import/export file buffer
          	HSBUF DB 5000 DUP (48)    	;buffer for help screens and DX input
		HEBUF DB 4010 DUP (48)		;buffer to store screen on help entry
          	IN_BUF  DB 155 DUP (48)   	;buffer for current voice selection
          	INIT_BUF DB 155 DUP (48)  	;buffer for initialisation parameters
          	RAND_BUF DB 160 DUP (48)  	;buffer for randomise voice
          	RANRES_BUF DB 155 DUP (48)	;buffer for resetting randomised voice
          	SCBUF DB 50 DUP (48)      	;buffer for current cursor contents
		SDABUF DB 2 DUP (48)
          	HEADER DB 50 DUP (48)     	;buffer for file header
          	COMCUR_BUF DB 50 DUP (48) 	;command cursor
          	COMSC_BUF DB 480 DUP (48) 	;command screen buffer
		TSRBUF DB 4010 DUP (48)   	;screen buffer for TSR
		CBRKBUF DB 160 DUP (48)   	;saves line of cntrl brk output

	


          	DATA ENDS

          	CODE SEGMENT

	  	JMP START_HERE		;jump to start of main control loop

		;;Soundblaster variables

		wCtIntNum               DW  5
	    	bSBC_INT_MASK_DATA      DB  ?
    		wIntMaskPort            DW  ?
    		wSoftwareInt            DW  ?
    		bIntMaskBit             DB  ?
    		ORG_INT_OFS             DW  ?
    		ORG_INT_SEG             DW  ?


		; Variables required for TSR routines

          	C_SS DW ?           	;Stack Segment for DXPRO
          	C_SP DW ?           	;Stack Pointer for DXPRO
          	C_DS DW ?           	;Data Segment for DXPRO
          	C_ES DW ?           	;Extra Segment for DXPRO


          	DXPRO DW ?          	;Address of DXPRO reentry point

          	HOTKEY DW 10         	;Hotkey definition
	  	UREC_MODE DB 0       	;flag set if Promidi is recording
	  	TSRFLG DB 0	       	;TSR flag

          	C_DTA_OFF DW ?      	;DXPRO disk transfer address
          	C_DTA_SEG DW ?      	;DXPRO disk transfer address
	  	C_PSP DW ?	      	;DXPRO PSP segment

          	D_DTA_OFF DW ?      	;PROMIDI disk transfer address
          	D_DTA_SEG DW ?      	;PROMIDI disk transfer address
	  	D_PSP DW ?	      	;PROMIDI PSP segment

          	DOSEG DW ?          	;DOS address for cntrl break handler
          	DOSOFF DW ?

          	INDOS_PTR DD 0         	;Pointer to DOS "indos" flag
          	INT28_VEC DD 0         	;Old interrupt 28 vector
          	INT13_VEC DD 0         	;Old interrupt 13 vector
          	INT09_VEC DD 0         	;Old interrupt 09 vector
	  	IRQ_vec DD 0	 	;Address of IRQ vector used for MPU calls

          	BUSY DB 0           	;Flag to prevent recursive TSR calls
          	IN_BIOS DB 0        	;Flag to indicate int 13H activity

          	DOS_SS  DW ?        	;saves PROMIDI's SS
          	DOS_SP  DW ?        	;saves PROMIDI'S SP
		_SDA_DSEG  DW ?	      ;saves PROMIDI'S DS

	  	DIRECTDX DB 67 DUP (32)   ;buffer for DXPRO path
	  	DIRECTPM DB 67 DUP (32)   ;buffer for PROMIDI path

; String data variables

		;file names

          	NAM DB 45,0,60 DUP (0) 		    	  	;DX input file name
          	LIBFIL DB "DXLIB.DAT",0           		;Voice Library file
          	SCREENS DB "DX00    .BIN",0

		ALSENT DB "Please Wait"
		BSEMSG DB "           SELECT A VOICE WITH CURSOR            "
		BLEMSG DB "          PRESS <L> TO LOAD NEW VOICES           "
		CBMSG DB "         TERMINATE ? Press <E> to leave DXPRO,any other key to resume           "
		CHKMSG DB "CHECKSUM ERROR : PRESS RETN. TO CONTINUE <ESC> TO RETRY "
		COMMSG DB " Use space bar or key letter to select command .<ESC> returns to data entry    "
		CURTR1 DB " TRANSLATION:CURSOR VALUE "
		CURTR2 DB" DX DISPLAY EQUIVALENT  "
		DENTRY DB "Use cursor keys to select & press + or - to change values,or <ESC> for commands "
		DOUC_MSG DB "AX VALUE  "
        	DOWNBK DB "DOWNLOADING VOICE NUMBER"
        	DY DB " "   ;dummy variable for input routines
		efilemsga DB "Press <P> to pack voices,<Esc> to abort, any other key for unpacked"
		efilemsgb DB "Press <H> to add headers,<Esc> to abort, any other key for no headers"
		ENDMSG DB "    Leave DXPRO now ?  Press <Y> to end or any other key to continue   "
        	EMSG DB "    DATA READ IN FROM DX OK          "
        	EOCOP DB "Press <CR> to copy envelope, <O> to copy operator "
        	EOINP DB "Enter target operator number (1-6) "
        	ERRMSG DB "SORRY SOMETHINGS WRONG :ERROR CODE= "
        	FILEMSG DB "File name ( [D:] [PATH]  NAME  ) "
		FEXIST DB"FILE ALREADY EXISTS,PRESS ANY KEY TO ABANDON OPERATION,<Y> FOR NEW FILE"
		FNEXISTMSG DB "Can't find file, Press <CR> to retry or <ESC> to abort "
        	FNAVAL DB "SCREEN FILE DX??.BIN NOT FOUND"
		INMSG DB "Sorry,incorrect format-press <CR> to retry <ESC> abort"
		INMSG2 DB"Ready to receive - press any key when DX is ready     "

		ifilemsg db "Press <A> to append or <R> to replace"
		invalifile db "Invalid file type, press <Esc> to abort any other key to retry"
	     fileopenerr db "File not found/empty:press <Esc> to abort,any other key to retry"
		fileunavail db "File already exists:press <Esc> to abort,<R> to replace"
		MSG DB   "      Please send data now                            "
		INRECMSG DB"      Can't send data when playing/recording      "
		INSTERR DB"CAN'T INSTALL DXPRO - TERMINATING       "
        	IRQMSG DB "NO DATA RECEIVED:PRESS RETN. TO CONTINUE <ESC> TO RETRY "
		NOMIDCD DB" MPU-401  NOT RESPONDING   : Press <ESC> to abandon or any other key to continue "
		MPUSETOK	  DB" MPU RESPONDED AT PORT 0330h. IRQ 5  WILL BE USED FOR INPUT"
		NOSDACFG DB "     CAN'T FIND SDA.CFG : Press <ESC> to abandon or any other key to continue    "
        	NOTAV DB"OPTION NOT AVAILABLE - SORRY ! "
        	OMSG DB   "    Sending data      "
        	OUTSEL DB " Press <CR>  to send this voice,<B> to send bank    "
 		PNTERR DB "   Printer not ready/available,press any key to continue    "
        	PNTVC DB "Press <CR> to print voice,<B> for bank"
 		PRESKY DB "       Press any key when ready          "
 		RANSEL DB "Press <CR> to randomise envelope data,<V> for all operator data"
 		RANCON DB "  Press <ESC> to reset,<CR> to use current voice ,any other key to continue     "
 		RANOP DB "Select operator in range 1-6,or press <A> for all operators"
 		RANAUTO DB "Press <ESC> to stop randomisation"
 		RLIBSEL DB "Press <CR> to repeat,<ESC> to abort,or any other key to select voice"
		SELMSG DB "Press <F> to input from a file,<ESC> to abort,any other key to input from DX"
        	SMSG DB "            Reading                  "
        	THANKS DB "Thanks for using DXPRO,Bye Now"
        	TMEOUT DB "   No data received,press any key to continue   "
        	TMSG DB "     Data read to main voice buffer  "
		TSRINST DB "DXPRO installed - Press  <Alt> <Left Shift> to activate",13,10,"$"
		TSRINSE DB "TSR installation error - terminating  "
		TSRQ DB "  PRESS <T> to make DXPRO memory resident, any other key to continue    "
		HIMSG DB "HI THERE SO FAR SO GOOD !!",13,10,"$"
		TSBUSY DB "DXPRO can't be loaded now - try again later","$"
		VWRITE DB "Put the cursor where you want to overwrite and press <CR>              " 
		VBWRITE DB "Locate using cursor then press <CR> for voice or <B> for bank overwrite"
        	VLAV DB "Press <CR> to load voice,<B> for bank"
        	VSAL DB "Press <CR> for EoF save,<O> overwrite        "
        	VSAV DB "Press <CR> to save voice,<B> for bank        "
        	VCMSG DB "Do you want to send init voice,press <CR> for Yes,<ESC> for No  "
        	VEMSG DB "THERE'S NO DATA TO READ,READ IN FROM DX OR FILE FIRST"

;         Word/Byte variables


          	ASC EQU 91
          	BANKNO DW 0           	;lowest number for bank on current display
	  	ATTR DB 0             	;screen attribute at cursor
          	BANKS DW 0
	  	BDOSEG DW ?			;BIOS cntrl brk segment
	  	BDOSOFF DW ?		;BIOS cntrl break offset
          	BIND DW 0           	;flag to determine whether 1/32 voices load/save
          	BNDIV DW 128           	;bank divisor
          	BROW DW 3200          	;lower row limit for cursor movement
          	BTABL DW 0            	; for bit unpacking tables address
          	CAP DW 0              	;cap variable to identify non numeric tables
	  	CBFLG DB 0			;DOS cntrl brk flag
          	CHECK DB 0            	;two's complement of sum of bytes
          	CMOV DW 20            	;default cursor movement
          	COMCUR DW 3558        	;command cursor location
          	CPOS  DW 998          	;initial cursor position
          	CR EQU 13             	;carraige return
          	CSIZE DW 5            	;variable for cursor size
          	COUNT_V1 DW 0         	;counters used in unpacking data
          	COUNT_V2 DW 0
          	COUNT_V3 DW 0
          	DIR DB 0              	;direction indicator for cursor movement
	  	DRIVEDX DB 0		;DXPRO's default drive
	  	DRIVEPM DB 0		;PROMIDI's default drive
		Dsr equ 128             ;Data Send Ready     READY WHEN DSR BIT IS SET
		Drr equ  64             ;Data Receive Ready  READY WHEN DRR BIT IS SET
          	DXINPUTFILE_OFFSET Dw 0	;bytes offset at start of packed bank for header (6)
		dxpacked db 0		;indicates type of input file
		ERRW DW 0             	;error number for file op failure
          	ESC EQU 27
		exphandle dw 0		;handle of export file
          	FILPT DD 0			;used to store offset of first space in bank
	  	FILSIZ DD 0
		form1 db 0			;output format parameter
		form2 db 0			;output format parameter
	  	packed_flag DB 0		;flag to indicate whether input or file data is packed (1)
          	HANDLE DW 0
	  	HANDLEH DW 0		;help screen file handle
		header_count DW 0		;counter for sysex header used in ISR
		HUNDRED DB 100
		imphandle dw 0		;handle of import file
		impsize1 dw 0		;MSB of import filesize
		impsize2 dw 0		;LSB of import file size
	  	INT_FLAG DB 0		;flag set when expecting and reading sysex
	  	IRLOC2	DW (8+2)*4
	  	IRLOC4	DW (8+4)*4
	  	IRLOC5	DW (8+5)*4
	  	IRLOC7	DW (8+7)*4
          	IRQ_LEVEL DB 0dh    	;irq number to be used (default 5)
	  	IRQ2FLG DB 0 		;flag to indicate if IRQ handler installed (99)
          	LCOL DW 40            	;default left column limit
          	LF EQU 10
          	LINE DW 160           	;line length used for division
	  	LPOS DW 806			;library cursor position
          	LRIND DB 0            	;toggle indicator for data entry
          	MI_LOC DW 0           	;interrupt addresses for Midicard
          	MI_BIT DB 0
	  	MOUSE1 DB 0			;mouse button 1 default
	  	MOUSE2 DB 0			;mouse button 1 default
	  	MOUSE3 DB 0			;mouse button 2 default
	  	MOUSE4 DB 0			;mouse button 2 default
	  	MPU_FLAG DB 0		;flag to indicate MPU ready for read/write
	  	MPUCMND DB 0		;command number to be sent to MPU
          	NOVCS DW 0            	;no. of voices in buffer
	  	NOTE DB 0			;test note for direct output
          	OPADJ DW 0            	;adjust offset in print buffer trans. table
          	OUTVC DB 1            	;voice selection for DX output
          	PAGC Dw 0

          	PORT_MPU401 DW ?        ;port address for MPU data
          	STATUSPORT_MPU401 DW ?  ;port address for MPU status/control

          	QUIT DW 0             	;flag to quit in command loop
          	RANST DW 0            	;random number locator
          	RANENOP DB 0          	;randomise env. or all op data flag
          	RANOPA DW 0           	; 1 or all operators flag
          	RCOL DW 144           	;default right column limit
          	ROW DB 160
          	SECS EQU 100
          	SERR DB 0             	;status error variable
	  	SIXTEEN DW 16
          	SOUND DB 0             	;flag for playing voice during edit
        	sysex_count DW 0		;buffer counter for sysex bytes
	  	TUP_STATUS DB 0	 	;tempo update status flag
          	TEN DB 10
		thandle dw 0		;file handle passed to function read_32voices for local use
          	THREE DW 3
		THOUSAND DW 1000
		timer_flag db 0		;bios flag used in timer routine pldel
          	TWENTY DB 20
          	TROW DW 318           	;upper row limit for cursor movement
          	VDST DB 0             	;target datapoint for ASCII translation
          	video_mode db 0					;stores the video mode on entry
		video_bufaddr dw 0b800h				;address of video buffer
		VNUM DW 0             	;selected voice number
          	VOICE DW 155          	;default number of bytes per voice
          	VOICENO DW 0          	;current voice selection from lib. file




;         Long variable lists

impfiletype dw 4960,5216,4096,4104,128,155,163		;array containing permissible bank/voice lengths (6 words)


ENV_DATA  DB 0,1,2,3,4,5,6,7,8,9,10
          DB 99,99,99,99,99,14,99,99,16,99

NON_ENV  DB 102,103,104,105,106,107,108,109,99,99,99
         DB 112,113,114,115,99,99,99,117,118,119,120
         DB 121,122,123,124,125,126,127

UNP_ENV  DB 10,0,1,11,9,2,3,11,8,0,2,12,7,0,1
         DB 13,6,2,4,13,4,0,0,15,3,1,5,15,1,3,6,12

UNP_NENV  DB 21,0,4,110,20,0,2,111,19,3,3,111,14,0,0,116
          DB 13,1,3,116,12,4,6,116

     VTABL DB 16,70,16,73,16,75,16,78,17,70,17,73,17,75,17,78
           DB 12,73,11,73,11,78,10,73,10,78,12,78,14,73,14,78
           DB 18,73,8,73,7,73,7,78,8,78

           DB 16,60,16,63,16,65,16,68,17,60,17,63,17,65,17,68
           DB 12,63,11,63,11,68,10,63,10,68,12,68,14,63,14,68
           DB 18,63,8,63,7,63,7,68,8,68

           DB 16,50,16,53,16,55,16,58,17,50,17,53,17,55,17,58
           DB 12,53,11,53,11,58,10,53,10,58,12,58,14,53,14,58
           DB 18,53,8,53,7,53,7,58,8,58

           DB 16,40,16,43,16,45,16,48,17,40,17,43,17,45,17,48
           DB 12,43,11,43,11,48,10,43,10,48,12,48,14,43,14,48
           DB 18,43,8,43,7,43,7,48,8,48

           DB 16,30,16,33,16,35,16,38,17,30,17,33,17,35,17,38
           DB 12,33,11,33,11,38,10,33,10,38,12,38,14,33,14,38
           DB 18,33,8,33,7,33,7,38,8,38

           DB 16,20,16,23,16,25,16,28,17,20,17,23,17,25,17,28
           DB 12,23,11,23,11,28,10,23,10,28,12,28,14,23,14,28
           DB 18,23,8,23,7,23,7,28,8,28

           DB 21,20,21,23,21,30,21,33,21,50,21,53,21,60,21,63
           DB 2,38,2,53,2,78,20,33,20,43,20,53,20,63,21,73
           DB 20,23,20,73,2,68


;CAP_TABL used to define maximum data entry values on edit screen


  CAP_TABL DW  31,99,1,14,0,0,3,3,99,99,99,7,0,0,3,7,0,0,99,99,99,99,99
           DW  228,31,258,7,288,48,308,1
           DW  3078,4,3098,99,3118,99,3138,99,3158,99,3178,7
           DW  3238,99,3258,99,3298,99,3318,99,3338,1

;DIS_TABL used to determine whether current data requires non numeric display


  DIS_TABL DW  0,0,0,1,0,0,2,2,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0
           DW  228,4,258,0,288,3,308,0
           DW  3078,5,3098,0,3118,0,3138,0,3158,0,3178,0
           DW  3238,0,3258,0,3298,0,3318,0,3338,0

;ATABLx used to set alpha display values

ATABL1  DB 2,"-7-6-5-4-3-2-1 0+1+2+3+4+5+6+7"
ATABL2  DB 4,"-LIN-EXP+EXP+LIN"
ATABL3  DB 4,     "A-1 A#-1B-1 "
                DB"C 0 C#0 D 0 D#0 E 0 F 0 F#0 G 0 G#0 A 0 A#0 B 0 " 
                DB"C 1 C#1 D 1 D#1 E 1 F 1 F#1 G 1 G#1 A 1 A#1 B 1 " 
                DB"C 2 C#2 D 2 D#2 E 2 F 2 F#2 G 2 G#2 A 2 A#2 B 2 " 
                DB"C 3 C#3 D 3 D#3 E 3 F 3 F#3 G 3 G#3 A 3 A#3 B 3 " 
                DB"C 4 C#4 D 4 D#4 E 4 F 4 F#4 G 4 G#4 A 4 A#4 B 4 " 
                DB"C 5 C#5 D 5 D#5 E 5 F 5 F#5 G 5 G#5 A 5 A#5 B 5 " 
                DB"C 6 C#6 D 6 D#6 E 6 F 6 F#6 G 6 G#6 A 6 A#6 B 6 " 
                DB"C 7 C#7 D 7 D#7 E 7 F 7 F#7 G 7 G#7 A 7 A#7 B 7 " 
                DB"C 8 "

ATABL4  DB  2,"0102030405060708091011121314151617181920212223242526272829303132"
ATABL5  DB  9," TRIANGLE SAW DOWN   SAW UP     SINE   S/HOLD"


ATABL6 DB "100010231047107210961122114811751202123012591288131813491380141314451479"
       DB "1514154915851622166016981738177818201862190519501995"
       DB "20422089213821882239229123442399245525122570263026922716281828842951"
       DB "3020309031623236331133883467354836313715380238903981"
       DB "407441694266436544674571467747864898"
       DB "501251295248537054955623575458886026616663106457660767616918"
       DB "70797244741375867762794381288318851187188913912093339550977297729772"



;Table to identify cursor locations from single letter input

CURLET DW 73,3558,79,3578,82,3598,87,3618,70,3638,70,3658
       DW 78,3718,80,3738,65,3758,81,3778


PNTABL DW 1,471,1,474,1,477,1,480,1,484,1,487,1,490,1,493
       DW 1,505,1,518,1,521,0,509,0,514,0,531,0,539,0,536
       DW 1,524,0,497,1,499,1,502,1,527

       DW 1,259,1,262,1,265,1,268,1,289,1,292,1,295,1,298
       DW 1,89,0,226,0,209,1,101,1,112,1,121,1,132,0,175
       DW 0,188,0,160,1,149

;Random number table

RANDO     DB 5,4,4,6,3,2,2,6,6,2,6,5,9,0,5,7,0,6,3,9,7,9,3,6,5,6,7,3,8,2
          DB 2,9,0,8,5,6,9,8,3,1,4,7,0,5,8,0,8,1,8,6,1,5,3,8,9,8,5,2,0,5
          DB 1,8,8,5,0,3,9,2,2,6,4,2,2,4,9,9,0,6,6,9,9,6,3,2,5,2,3,2,4,8
          DB 6,0,9,3,3,2,6,9,2,7,8,5,9,4,1,4,0,7,5,6,8,2,4,1,4,0,2,0,1,5
          DB 1,3,8,5,8,7,8,0,3,0,1,6,2,6,9,6,5,9,7,8,0,1,3,8,5,1,5,3,4,5
          DB 6,1,1,4,9 6,9,4,4,0,1,1,2,8,6,8,8,2,1,8,5,8,9,2,5,0,3,6,3,8
          DB 5,2,8,6,2,6,2,7,3,3,3,3,4,5,1,7,7,4,5,5,0,5,2,1,9,8,1,6,1,9
          DB 1,0,6,5,1,6,7,0,7,9,9,2,5,1,1,5,9,8,8,8,8,4,5,0,2,7,2,0,9,5
          DB 8,3,4,6,3,7,5,5,7,7

          DB 4,1,4,1,7,9,8,3,2,6,8,7,7,1,9,9,2,2,9,4,4,6,6,1,4,5,0,9,4,8
          DB 6,4,8,8,6,2,0,0,0,2,9,7,3,6,5,3,0,9,7,6,2,8,3,5,7,9,4,0,7,0
          DB 2,0,6,5,2,3,5,7,7,4,1,6,2,4,9,7,5,0,1,9,2,1,1,4,5,0,5,2,1,7
          DB 4,7,2,8,6,7,6,3,0,5,1,7,7,8,3,0,0,0,1,5,1,0,8,0,6,8,3,0,9,1
          DB 9,1,5,3,0,3,6,4,6,6,3,9,9,8,1,6,2,4,8,1,4,9,1,7,7,7,5,7,7,9
          DB 4,0,9,5,0,8,4,2,8,0,2,9,8,8,1,8,5,9,6,6,6,2,8,0,0,7,0,3,2,6
          DB 8,4,7,4,0,6,2,6,6,0,7,7,3,7,9,9,0,2,7,9,8,2,9,9,5,6,4,1,5,7
          DB 6,6,1,6,4,4,1,1,8,0,1,0,0,8,9,4,1,7,5,7,7,8,2,5,8,9,6,4,8,8
          DB 8,8,6,2,9,3,7,2,3,1

          DB 9,6,7,5,4,1,7,6,7,6,5,5,6,5,9,4,4,1,0,5,4,7,3,6,1,3,4,8,3,3
          DB 8,6,6,7,9,2,3,9,3,0,5,3,2,4,9,2,7,0,8,3,3,4,3,5,7,8,8,0,4,0
          DB 5,3,3,6,4,7,1,7,2,6,4,2,6,9,0,6,6,3,3,4,6,0,3,3,2,2,2,5,5,4
          DB 9,0,6,0,0,7,1,1,1,3,0,6,3,1,8,3,7,4,0,3,4,9,9,2,7,5,7,7,1,5
          DB 5,0,4,2,3,6,7,3,7,2,6,3,1,1,6,4,8,8,8,8,2,1,5,0,5,8,0,1,8,2
          DB 6,2,1,1,1,5,2,8,2,0,0,7,2,4,3,7,9,9,3,1,8,9,2,9,2,8,4,7,6,7
          DB 8,5,6,9,6,7,3,9,4,7,2,2,2,7,8,1,1,5,5,1,4,7,5,3,4,0,9,2,4,3
          DB 6,7,8,7,9,0,0,5,4,4,2,3,4,1,0,1,2,7,4,0,0,2,5,4,0,5,4,4,4,0
          DB 3,2,9,4,9,1,3,4,9,1

          DB 9,8,6,1,4,7,5,9,9,3,8,4,4,6,0,6,2,8,4,6,5,9,8,4,4,1,4,9,2,2
          DB 4,8,7,3,0,7,3,4,4,3,4,8,1,6,7,3,4,7,7,0,2,4,8,5,6,0,3,6,4,8
          DB 4,4,9,8,9,0,9,3,5,1,9,8,7,9,5,1,8,6,4,4,3,9,7,6,5,7,1,0,5,8
          DB 9,0,3,6,8,4,4,1,0,4,9,6,8,8,7,1,2,4,7,9,8,0,6,2,1,6,6,2,2,3
          DB 8,6,0,8,5,7,8,2,8,5,0,2,4,3,2,5,3,3,4,3,4,2,8,4,6,9,4,7,7,1
          DB 9,0,8,0,1,2,1,4,7,2,4,2,8,1,5,7,7,4,0,8,3,7,3,9,0,7,6,7,6,6
          DB 5,2,6,1,5,3,2,1,4,1,3,0,2,6,8,1,8,1,0,6,5,5,1,6,5,7,7,3,1,2
          DB 8,3,6,6,6,3,6,0,2,8,2,8,4,2,0,7,0,2,1,9,8,1,3,6,9,4,1,9,4,3
          DB 4,7,3,6,6,4,1,0,6,7

;Buffer used to intialise 'blank' voices in library file for current bank

BLVC      DB 255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
          DB   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
          DB   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
          DB   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
          DB   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
          DB   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32
          DB  32,32,32,32,32,32

;Print Voice Buffer

PNTV_BUF DB 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,"V","o","i","c","e",32,"N","a","m","e"
DB 32,"=",32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32
DB 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,13,10

DB "A","l","g",32,"=",32,32,32,"/",32,"S","p","e","e","d",32,"=",32,32,32      ;Line 2 1-20
DB 32,"/",32,"D","e","l","a","y",32,"=",32,32,"/","P","M","D",32,"=",32,32    ;Line 2 21-40
DB 32,32,"/",32,"A","M","D",32,"=",32,32,32,32,"/",32,"T","r","a","n","s"      ;Line 2 41-60
DB "p","o","s","e",32,"=",32,32,32,13,10                                  ;Line 2 61-end

DB "P","M","S",32,"=",32,32,32,"/",32,"L","F","O",32,"S","Y","N","C",32,"="  ;Line 3 1-20
DB 32,32,32,32,32,"/",32,"W","a","v","e",32,"=",32,32,32,32,32,32,32      ;Line 3 21-40
DB 32,32,32,"/",32,"O","S","C",32,"S","Y","N","C",32,"=",32,32,32,32,32   ;Line 3 41-60
DB "/",32,"F","e","e","d","b","a","c","k",32,"=",32,32,13,13,10              ;Line 3 61-end

DB "P","i","t","c","h","E","G",32,32,32,":",32,32,"R","a","t","e","s",32  ;Line 4 1-20
DB 32,"(","1","-","4",")",32,"=",32,32,32,32,32,32,32,32,32,32,32,32                ;Line 4 21-40
DB ":",32,32,"L","e","v","e","l","s",32,32,"(","1","-","4",")",32,"=",32,32  ;Line 4 41-60
DB 32,32,32,32,32,32,32,32,32,32,13,10,13,10,13,10                              ;Line 4 61-end

DB 32,32,32,32,32,32,"E","G",32,"R","a","t","e",32,"1","-","4",32,32,"L"        ;Line 7 1-20
DB "e","v","e","l",32,"1","-","4",32,32,32,32,"M",32,"F","r","e","q",32,32    ;Line 7 21-40
DB "B","P",32,"C","u","r","v","e",32,"L","-","R",32,"D","e","p","t","h",32,"O" ;Line 7 41-60
DB "p",32,"D","t",32,"K","R","S",32,"K","V","S",32,"A","M","S",13,10,13,10   ;Line 7 61-end

DB 32,32,32,32,32,32,"-","-","-","-","-","-","-","-","-","-","-",32,32,"-"         ;Line 9 1-20
DB "-","-","-","-","-","-","-","-","-","-",32,32,"-",32,"-","-","-","-","-",32 ;Line 9 21-40
DB "-","-",32,"-","-","-","-","-","-","-","-","-",32,"-","-","-","-","-",32,"-" ;Line 9 41-60
DB "-",32,"-","-",32,"-","-","-",32,"-","-","-",32,"-","-","-",13,10,13,10       ;Line 9 61-end

DB "O","P","6",32,"-",32,32,32,32,32,32,32,32,32,32,32,32,32,32,32                    ;Line 11 1-20
DB 32,32,32,32,32,32,32,32,32,32,32,32,"R",32,32,32,32,32,32,32                           ;Line 11 21-40
DB 32,32,32,"-","L","I","N",32,"-","L","I","N",32,32,32,32,32,32,32,32            ;Line 11 41-60
DB 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,13,10                           ;Line 11 61-80

DB "O","P","5",32,"-",32,32,32,32,32,32,32,32,32,32,32,32,32,32,32                     ;Line 12 1-20
DB 32,32,32,32,32,32,32,32,32,32,32,32,"R",32,32,32,32,32,32,32                       ;Line 12 21-40
DB 32,32,32,"-","L","I","N",32,"-","L","I","N",32,32,32,32,32,32,32,32            ;Line 12 41-60
DB 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,13,10                           ;Line 12 61-80

DB "O","P","4",32,"-",32,32,32,32,32,32,32,32,32,32,32,32,32,32,32                     ;Line 13 1-20
DB 32,32,32,32,32,32,32,32,32,32,32,32,"R",32,32,32,32,32,32,32                           ;Line 13 21-40
DB 32,32,32,"-","L","I","N",32,"-","L","I","N",32,32,32,32,32,32,32,32            ;Line 13 41-60
DB 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,13,10                           ;Line 13 61-80

DB "O","P","3",32,"-",32,32,32,32,32,32,32,32,32,32,32,32,32,32,32                     ;Line 14 1-20
DB 32,32,32,32,32,32,32,32,32,32,32,32,"R",32,32,32,32,32,32,32                           ;Line 14 21-40
DB 32,32,32,"-","L","I","N",32,"-","L","I","N",32,32,32,32,32,32,32,32             ;Line 14 41-60
DB 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,13,10                           ;Line 14 61-80

DB "O","P","2",32,"-",32,32,32,32,32,32,32,32,32,32,32,32,32,32,32                     ;Line 15 1-20
DB 32,32,32,32,32,32,32,32,32,32,32,32,"R",32,32,32,32,32,32,32                           ;Line 15 21-40
DB 32,32,32,"-","L","I","N",32,"-","L","I","N",32,32,32,32,32,32,32,32            ;Line 15 41-60
DB 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,13,10                           ;Line 15 61-80

DB "O","P","1",32,"-",32,32,32,32,32,32,32,32,32,32,32,32,32,32,32                     ;Line 16 1-20
DB 32,32,32,32,32,32,32,32,32,32,32,32,"R",32,32,32,32,32,32,32                           ;Line 16 21-40
DB 32,32,32,"-","L","I","N",32,"-","L","I","N",32,32,32,32,32,32,32,32             ;Line 16 41-60
DB 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,13,10,13,10,13,10,13,10               ;Line 16 61-83

COM_BUF DB 32,31,"C",31,"o",31,"m",31,"m",31,"a",31,"n",31,"d",31,0,31,"L",31         ;23.1
DB "i",31,"s",31,"t",31,0,31,":",31,0,31,0,31,0,31,0,31,0,31                         ;23.2
DB 0,31,"I",31,"N",31,"P",31,"U",31,"T",31,0,31,0,31,0,31,0,31                       ;23.3
DB 0,31,"O",31,"U",31,"T",31,"P",31,"U",31,"T",31,0,31,0,31,0,31                     ;23.4
DB "R",31,"E",31,"A",31,"D",31,0,31,0,31,0,31,0,31,0,31,0,31                 		 ;23.5
DB 0,31,"W",31,"R",31,"I",31,"T",31,"E",31,0,31,0,31,0,31,0,31                       ;23.6
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                       		 ;23.7
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                         		 ;23.8


DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;24.1
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;24.2
DB 0,31,"P",31,"R",31,"I",31,"N",31,"T",31,0,31,0,31,0,31,0,31                       ;24.3
DB 0,31,"P",31,"L",31,"A",31,"Y",31,0,31,0,31,0,31,0,31,0,31                         ;24.4
DB "R",31,"A",31,"N",31,"D",31,"O",31,"M",31,"I",31,"S",31,"E",31,0,31               ;24.5
DB 0,31,"Q",31,"U",31,"I",31,"T",31,0,31,0,31,0,31,0,31,0,31                         ;24.6
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;24.7
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;24.8

DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.1
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.2
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.3
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.4
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.5
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.6
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.7
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.8


LCOM_BUF DB 0,31,"C",31,"o",31,"m",31,"m",31,"a",31,"n",31,"d",31,0,31,"L",31         ;23.1
DB "i",31,"s",31,"t",31,0,31,":",31,0,31,0,31,0,31,0,31,0,31                         ;23.2
DB 0,31,"I",30,"N",31,"P",31,"U",31,"T",31,0,31,0,31,0,31,0,31                       ;23.3
DB 0,31,"O",30,"U",31,"T",31,"P",31,"U",31,"T",31,0,31,0,31,0,31                     ;23.4
DB 0,31,"R",30,"E",31,"A",31,"D",31,0,31,0,31,0,31,0,31,0,31                         ;23.6
DB 0,31,"W",30,"R",31,"I",31,"T",31,"E",31,0,31,0,31,0,31,0,31                       ;23.7
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31        			         ;23.5
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                         ;23.8


DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;24.1
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;24.2
DB 0,31,"P",31,"R",31,"I",31,"N",30,"T",31,0,31,0,31,0,31,0,31                       ;24.3
DB 0,31,"P",30,"L",31,"A",31,"Y",31,0,31,0,31,0,31,0,31,0,31                         ;24.4
DB "R",31,"A",30,"N",31,"D",31,"O",31,"M",31,"I",31,"S",31,"E",31,0,31               ;24.5
DB 0,31,"Q",30,"U",31,"I",31,"T",31,0,31,0,31,0,31,0,31,0,31                         ;24.6
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;24.7
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;24.8

DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.1
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.2
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.3
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.4
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.5
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.6
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.7
DB 0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31,0,31                                 ;25.8


; T1.TXT   MACROS

;Program begins with Macros Section

          MESSAGE MACRO

	  	CALL VID_TEST
          MOV DI,#1           ;screen location for start of message
          MOV AH,#2           ;attribute for messsage characters
          CLD
          MOV SI,OFFSET #3    ;address of text of message
          MOV CX,#4           ;number of bytes in text

S1:       LODSB               ;load single character from source
          STOSW               ;write character and attribute
          LOOP S1

          MOV AH,02H          ;now set cursor position
          MOV BH,0
          MOV DH,#5           ;row
          MOV DL,#6           ;column
          INT 10H
          #EM

         INPUT MACRO

	    CALL VID_TEST
          MOV DI,#1           ;screen location for start of message
          MOV AH,#2           ;attribute for messsage characters
          MOV SI,OFFSET #3    ;address of text of message
          MOV CX,#4           ;number of bytes in text

M4:       LODSB               ;load single character from source
          STOSW               ;write character and attribute
          LOOP M4

          MOV AH,02H          ;now set cursor position
          MOV DH,#5           ;row
          MOV DL,#6           ;column
          INT 10H
                  
;now get input and adjust any lower case to upper case

M5:       MOV AH,01
          INT 16H
          JZ M5

          MOV AH,0
          INT 16H

          MOV CX,AX       
          MOV DL,#7             ;0 if no letter check required or ASCII
          MOV AH,#8             ;0 if no ESC check required
          MOV DH,#9             ;0 if no CR check required
          CALL CHECKINP

          #EM


          BORDER MACRO

          MOV AH,0BH          ;colour palette function
          MOV BH,0
          MOV BL,#1           ;select border colour
          INT 10H
          #EM


          CURS MACRO          ;macro to move cursor

M0:       MOV AX,#1
          CURLS #1,COMCUR_BUF
          MOV DL,DIR
          CMP DL,1             ;see if we are adding or subtracting
          IF Z SUB AX,#2            ;move up line or back column
          CMP DL,1
          IF NZ ADD AX,#2           ;mov down line or up column
          MOV #1,AX
          

          #EM


          CSR MACRO

          MOV AH,2       ;function to position cursor
          MOV BH,0       ;page selector
          MOV DL,#1      ;x
          MOV DH,#2      ;y
          INT 10H

          #EM


          	DXOB MACRO

      	MOV DX,STATUSPORT_MPU401

M1:        	IN AL,DX
           	TEST AL,drr                 	; Output buffer empty ?
           	JNZ M1                       	; No - keep trying.

          	MOV DX,PORT_MPU401            ; Send byte.
          	MOV AL,#1
          	OUT DX,AL

          	#EM




;****************************** PLDEL***********************************************
;
;
	;routine uses BIOS interval timer to wait for required no milliseconds
;
;
	;input :CX,DX delay time required in milliseconds : DX not used
	;output : none, if timer isn't available carry flag is set
;
;*********************************************************************************


pldel:	push ax
        	push bx
		push es


pl20:		mov timer_flag,0			;reset timer flag
		mov al,0				;to set timer
		mov ah,083h				;bios function call
		mov bx, offset timer_flag	;flag bios sets when timer interval has elapsed
		mov es,cs				;bios expects segment address in ES
		int 15h				; start interval timer

		;timer interval loop

pl30:		mov al,timer_flag			;delay loop
        	cmp al,0                     	;check if interval has elapsed
        	je pl30
	
pl50:		mov al,1				;stop the interval timer
		mov ah,083h
		int 15h

		pop es
        	pop bx
		pop ax

		ret		



          DEL MACRO

          PUSH CX
          MOV CX,#1

M1:       PUSH CX
          MOV CX,300

M2:       MOV DX,0
          LOOP M2

          POP CX
          LOOP M1
          POP CX
          #EM







;screen character print macro

          SCREENPT MACRO

	  PUSH ES
	  PUSH BX
	  PUSH DI
	  PUSH AX
	  pushf

	  MOV BX,0b800h
	  MOV ES,BX
          MOV DI,#1
          MOV AX,#2
          ES MOV [DI],AX

	  popf
	  pop ax
	  pop di
	  pop bx
	  pop es


          #EM


;Routine SCREEN - ASCII converts binary data in AL to ASCII (0-255), and prints at
;screen location starting at point set in entry (second item), attribute is first

SCREEN_ASCII MACRO

	pusha

	mov bx,video_bufaddr		;set video address
	mov es,bx
	MOV BH,#2			;attribute byte
	MOV AH,0
	DIV HUNDRED			;AL has hundreds, AH remainder
	ADD AL,48
	MOV BL,AL
	MOV DI,#1			;set screen write location
	ES MOV [DI],BX			;write to screen
	MOV AL,AH			;put hundreds remainder in AI
	MOV AH,0
	DIV TEN				;AL has tens AH remainder
	ADD AL,48
	MOV BL,AL
	ES MOV [DI+2],BX
	MOV BL,AH			;transfer units
	ADD BL,48
	ES MOV [DI+4],BX

	popa

	#EM

          CURSOR MACRO

          MOV DI,OFFSET #4               ;offset for temporary buffer
          MOV CX,#1                      ;cursor size
	  CALL VID_TEST

M1:       ES MOV AX,[#2]      ;put character and attribute in buffer
          MOV [DI],AX         ;store them
          MOV AH,#3
          ES MOV [#2],AX      ;write to screen
          INC #2,2            ;next character
          INC DI,2
          LOOP M1
          #EM



          CURES  MACRO          ;macro to reset screen at cursor location

          MOV SI,OFFSET SCBUF
          MOV DI,CPOS
          MOV CX,CSIZE

M1:       MOV DX,W[SI]
          ES MOV AX,W[DI]
          MOV AH,DH             ;change attribute
          ES MOV W[DI],AX 
          INC SI,2
          INC DI,2
          LOOP M1
         
          #EM

          CURLS  MACRO          ;macro to reset library screen at cursor location

          MOV SI,OFFSET #2
          MOV DI,#1
          MOV CX,CSIZE
          REP MOVSW

          #EM


	BEEP MACRO

        push ax
	  push cx

	  mov     al,0b6h
        out     043h,al         ;write timer mode regester
        mov     ax,2712         ;set divisor value to 1000
        out     042h,al         ;write timer 1 count lsb
        mov     al,ah
        out     042h,al         ;write timer 2 count msb
        in      al,061h         ;get current port b setting
        mov     ah,al           ;save current setting.
        or      al,3            ;turn speaker on
        out     061h,al
        mov     cx,0A000h        ;10 msec delay on ibm pc 4.7mh 8088
m1:
        loop    m1
        mov     al,ah           ;get old port b setting
        out     061h,al         ;set it back the way you found it

	  pop cx
	  pop ax
        #em
        


          BLANK MACRO         ;to restore bottom on screen after message

          PUSH AX             ;store register
          MOV AH,31           ;attribute
          MOV AL,0
          MOV DI,3840         ;start of line 25
          MOV CX,80

M4:       ES MOV [DI],AX
          INC DI,2
          LOOP M4
          POP AX              ;restore register
          #EM

          LBLANK MACRO        ;to blank part of library screen after messages

          PUSH AX             ;store register
          MOV AH,31           ;attribute
          MOV AL,0
          MOV DI,3522         ;start of line 23
          MOV CX,71

M4:       ES MOV [DI],AX
          INC DI,2
          LOOP M4
          POP AX              ;restore register
          #EM

;macro to save screen between defined lines

          SCRSAV MACRO

          MOV DI,OFFSET #1                     ;storage buffer
	  CALL VID_TEST
          MOV AX,#2                     ;number of lines to save
          MUL LINE
          MOV CX,AX                     ;gives total bytes to save
          MOV AX,#3                     ;start row for screen save
          MUL LINE
          MOV SI,AX

M1:       ES MOV AL,[SI]
          MOV [DI],AL
          INC DI
          INC SI

          LOOP M1

;now get cursor position
	  PUSH DI

          MOV AH,3
          MOV BH,0
	  INT 10H

	  POP DI
          MOV [DI+1],DH              ;cursor row
          MOV [DI+2],DL              ;cursor column    
	  MOV [DI+3],CH		     ;cursor start line
	  MOV [DI+4],CL

          #EM

;macro to restore screen between defined lines

          SCRES MACRO

          MOV SI,OFFSET #1              ;storage buffer
	  CALL VID_TEST
          MOV AX,#2                     ;number of lines to restore
          MUL LINE
          MOV CX,AX                     ;gives total bytes to restore
          MOV AX,#3                     ;start row for screen restore
          MUL LINE
          MOV DI,AX

M1:	  MOV AL,[SI]
	  ES MOV [DI],AL
	  INC SI
	  INC DI

	  LOOP M1


	  PUSH SI
          
;now set cursor position

          MOV AH,2
          MOV BX,0
          MOV DH,[SI+1]              ;cursor row
          MOV DL,[SI+2]              ;cursor column    
	 
          INT 10H

	  POP SI

;cursor size

	MOV AH,1
	MOV CH,[SI+3]
	MOV CL,[SI+4]
	INT 10H

          #EM

          HEADBL MACRO        ;to blank defined areas of screen

          MOV BX,#1           ;starting position
          MOV CX,#2           ;number of rows to blank
          MOV DX,#3
          MOV AL,0            ;data byte
          MOV AH,#4           ;attribute byte
          
M1:       PUSH CX
          PUSH BX
          MOV CX,DX           ;number of columns

M2:       ES MOV [BX],AX
          INC BX,2
          LOOP M2
          
          POP BX
          POP CX
          ADD BX,160          ;jump to start point on next row
          LOOP M1

          #EM
          
;Macro to display buffer contents

          VCEPT MACRO

          CSR 0,24
          MOV SI,OFFSET #1
          MOV CX,#2
          MOV DX,0

M11:      PUSH CX             ;store main counter value

          MOV CX,#3           ;number of bytes per voice
          CALL PNT_VC         ;print a voice
          POP CX              ;retrieve main counter value
          MOV AL,CR           ;CR/LF sequence
          CALL PRT_VAL
          MOV AL,LF
          CALL PRT_VAL
          LOOP M11            ;read another

          #EM


;********************* Checksum Macro ***************************************
;
;
;	Determines 2's complement checksum on data in buffer
;
;***************************************************************************
                CHECKSUM MACRO

M1:       	MOV SI,OFFSET #1
          	MOV AX,0
          	MOV DX,0
          	MOV CX,#2
          	PUSH CX

M2:       	MOV AL,B[SI]              ;calculate sum of bytes
          	CBW
          	ADD DX,AX
          	INC SI
          	LOOP M2

M3:       	MOV AX,DX                   ;sum of bytes
          	NOT AX                      ;twos' complement
          	ADD AX,1
          	SHL AL,1                    ;zeroise MSB
          	SHR AL,1
          
M4:		MOV AH,03fH
          	SCREENPT #3,AX              ;calculated checksum
          	MOV CHECK,AL                ;checksum variable

M5:         MOV SI,OFFSET #1
          	POP BX
          	MOV AL,B[SI+BX]             ;checksum as in buffer
          	MOV AH,05fH
          	SCREENPT #4,AX

M6:         CMP AL,CHECK
          	JE #5

          	#EM




;                  T11.txt   : Miscellaneous routines

;                           HELP Screen Routines also
;			    PNT_VC,ASCPT,PRT_VAL,CHECKINP 
;			    C_BRK,CBHAND,C_BRKRES
;			    EE ,
;			    MOUSE,HI                            

     ;    Save screen and cursor location


     HELP:      
	        	DIV TEN         ;AL quotient, AH remainder
                	PUSH AX         ;Store screen index
                	CMP AX,0
                	JE H0           ;no need to save for opening sceen
                	CMP AL,9        ;data entry and library screens     
                	JE H0
                	SCRSAV HEBUF,25,0


     ;    Now get file title and amend suffix

     H0:  		CS MOV SI,OFFSET SCREENS
          		POP AX
          		PUSH AX             ;store for later use
          		ADD AH,48
          		ADD AL,48
          		MOV [SI+2],AL
          		MOV [SI+3],AH

     ;    Open File 

          		MOV AH,61
          		MOV AL,0
          		CS MOV DX,OFFSET SCREENS
          		INT 21H
          		JNC H2              ;if unsuccessful terminate

H1:	  		MOV AH,59H		;get extended error info
	  		MOV BX,0
	  		INT 21H

	  		PUSH ES
	  		CALL VID_TEST
          		MESSAGE 3360,124,FNAVAL,31,25,0
	  		POP ES
	  		POP AX
	  		MOV AX,0ffh
	  		RET

     ;    Read Screen to Buffer

H2:       		MOV HANDLEH,AX       ;store file handle
          		MOV AH,63           ;read from file
          		MOV CX,4000
          		MOV DX,OFFSET HSBUF
          		MOV BX,HANDLEH
          		INT 21H
          		JC H1               ;if unsuccessful terminate

          		MOV AH,62             ;close file
          		MOV BX,HANDLEH
          		INT 21H

     ;    Read Buffer to screen

	  		CALL VID_TEST
          		MOV CX,4000
          		MOV SI,OFFSET HSBUF
          		MOV DI,0
          		REP MOVSB            ;display screen

     ;  Set Border

			pop ax			;get screen number
			push ax
			mov bl,1		;default border color
			cmp ax,0		;opening screen
			jne h40
			mov bl,3
	

h40:			MOV AH,0BH          ;colour palette function
        		MOV BH,0
     			INT 10H


     ;return to command processor for data entry and library set up screens

        		pop bx  
			CMP BL,9
        		JE RET
        		CMP BX,0            ;the opening splash screen
			je ret
	

          		PUSH BX

     ;    Get Input

     H3:  		MOV AH,01
          		INT 16H
          		JZ H3
        
          		MOV AH,0
          		INT 16H             ;read waiting character

          		POP BX              ;screen number

          		CMP AL,27
          		JE H7

          		CMP AH,73           ;page up
          		JNE H4
          		CMP BH,1
          		JBE H6              ;if on first screen the back to program

          		DEC BH
          		PUSH BX             ;and stack
          		JMP H0              ;start again
	  
	  		RET

     H4:  		CMP AH,81
          		JNE H6
          		CMP BH,4            ;maximum number of screens
          		JB H5
          		MOV BH,1            ;reset to first help screem
          		PUSH BX
          		JMP H0

    H5:   		INC BH
          		PUSH BX
          		JMP H0

     H6:  		PUSH BX             ;replace screen number
          		BEEP                ;incorrect entry recycle
          		JMP H3


     H7:  		SCRES HEBUF,25,0          ;restore screen
          		RET                 ;return to main program


;Print routines associated with VCEPT macro

PNT_VC:   MOV AL,[SI]         ;move byte from buffer
          CMP AL,255
          JBE VC1
          MESSAGE 3840,79,ERRMSG,36,24,0
          MOV AH,76
          INT 21H

VC1:      MOV DX,CX           ;put counter value in DX
          PUSH CX             ;stack counter
          CMP DX,10           ;test for voice name in ASCII
          JA ASIC             ;if below  118 proceed with print
          CALL PRT_VAL        ;print string character
          JMP ASID

ASIC:     CALL ASCPT          ;print routine for ASCII value
          MOV AL,32           ;ASCII code for space
          CALL PRT_VAL        ;print two spaces after each number
          CALL PRT_VAL        ;print two spaces after each number

ASID:     POP CX              ;reset counter
          INC SI              ;increment buffer address
          LOOP PNT_VC          ;repeat to end of buffer
          MOV AL,CR           ;CR/LF sequence
          CALL PRT_VAL
          MOV AL,LF
          CALL PRT_VAL
          RET




;routine to print ASCII values as numbers

ASCPT:         MOV AH,0            ;reset
               DIV TEN             ;divide-AL contains tens,AH units.
               PUSH AX
               CMP AL,0            ;see if its <10
               JE VZ

               ADD AL,48           ;convert to ASCII value
               CALL PRT_VAL

VZ:            POP AX
               ADD AH,48
               MOV AL,AH
               CALL PRT_VAL

               RET

;routine to print ASCII value

PRT_VAL:       MOV AH,06
               MOV DL,AL
               INT 21H
               RET                 ;return


CHECKINP: CMP CL,97             ;adjust case of letter entry
          JB CHI0
          CMP CL,122
          JA CHI0               ;letter is lower case a-z
          SUB CL,32             ;convert to upper case
          CMP DL,0              ;see if letter check is reqwuired
          JE CHI0
          CMP CL,DL             ;see if right letter is input
          JE CHI2

CHI0:     CMP CH,0
          JE CHI2               ;no checking required
          CMP CL,27             ;ESC check
          JE CHI2
          CMP DH,0
          JE CHI2               ;no check required
          CMP CL,13             ;CR
          JE CHI2

CHI1:     BEEP
          RET


CHI2:     MOV AX,CX
          RET


; routine to enable cntrl break interrupts

C_BRK:	MOV AX,03300H       ;function number-get status fo cntrl brk flag
	  	INT 21H	      ;put in DL
	  	MOV CBFLG,DL
		
	  	MOV AX,03301H
          	MOV DL,0           ;set checking off
          	INT 21H             ;to DOS

          	PUSH ES             ;store registers
          	PUSH BX

          	MOV AH,35H          ;get interrupt vector function
          	MOV AL,1BH          ;for BIOS cntrl/brk detect
          	INT 21H

          	MOV BDOSEG,ES        ;store original address of handler
          	MOV BDOSOFF,BX
          	POP BX              ;restore registers
          	POP ES

          	MOV AH,25H          ;set new address for cntrl/brk handler
          	MOV AL,1BH
          	MOV DX,OFFSET CBHAND    ;route to handler
          	INT 21H             ;to DOS

	  	RET

CBHAND:     STI                      ;restore interrupts

		PUSH SI
		PUSH DI
		PUSH BP


CBH10:	MOV AX,CS
            MOV DS,AX
            MOV AL,20h               ;revive keyboard
 	      OUT 20H,AL
		CALL VID_TEST

CBH20:	MOV SI,3840		;save line where C brk message is output
		MOV DI,offset CBRKBUF
		MOV CX,160

CBH30:	ES MOV AL,[SI]
		MOV [DI],AL
		INC SI
		INC DI
		LOOP CBH30

          	MOV DI,3840           ;screen location for start of message
         	MOV AH,124           ;attribute for messsage characters
         	CLD
          	MOV SI,OFFSET CBMSG    ;address of text of message
          	MOV CX,80           ;number of bytes in text

CBH40:      LODSB               ;load single character from source
            STOSW               ;write character and attribute
            LOOP CBH40

cbh45:	MOV AH,01			;wait for user input
		INT 16H
		jz cbh45

		CMP AL,69			;"E" entered so terminate
		JE CBH60

		CMP AL,101
		JE CBH60

CBH50:	MOV SI,OFFSET CBRKBUF	;restore status line before resuming
		MOV DI,3840
		MOV CX,80
		REP MOVSW

		MOV AX,00040H		; ??
		MOV DS,AX
		POP BP
		POP DI
		POP SI
		IRET			;resume

CBH60:	PUSH DS
		PUSH DX

		
		CS MOV DS,BDOSEG	;reset BIOS cntrl brk handler
		CS MOV DX,BDOSOFF
		MOV AX,0251BH
		INT 21H

CBH70:	POP DX
		POP DS

		MOV AX,03301H		;reset cntrl brk flag
		MOV DL,CBFLG
		INT 21H


CBH80:      SCRES TSRBUF,25,0

cbh90:	cmp IRQ2FLG,99	;see if CBRK called before IRQ2 handler set
	  	je cbh100		;if not reset IRQ2

cbh95:	cmp mpu_flag,0ffh	;if no MIDI no need to rest vector
		je cbh100

		call reset_IRQ	 

CBH100:	POP BP
		POP DI
		POP SI

		MOV AX,04C00H
		INT 21H			;terminate



;Routine to restore Cntrl break addresses before terminating


C_BRKRES:	PUSH DS
		PUSH DX

		
		CS MOV DS,BDOSEG	;reset BIOS cntrl brk handler
		CS MOV DX,BDOSOFF
		MOV AX,0251BH
		INT 21H

		MOV AX,03301H		;reset cntrl brk flag
		CS MOV DL,CBFLG
		INT 21H

		POP DX
		POP DS

		RET


;general purpose error routines

EE:       MOV AH,89                         ;function for extended error info
          MOV BX,0
          INT 21H

ERT:      PUSH AX                           ;put error data in register
          MESSAGE 3840,116,ERRMSG,37,25,0    ;address of error message
          POP DX                            ;restore error code
          MOV VDST,DL                       ;put lower part of word in var
          CALL ASCPT                        ;print number
          MOV AH,76
          MOV AL,01
          INT 21H                           ;terminate


;Mouse routine to store/reset Mouse values on entry/exit


MOUSE:	 CMP AH,2
	 JE MO1

	 MOV AL,29		;reset values
	 MOV BL,MOUSE1
	 INT 21
	 MOV AH,1
	 MOV AL,30
	 MOV BL,MOUSE2
	 INT 21
	 MOV AH,1
         MOV AL,31
	 MOV BL,MOUSE3
	 INT 21
	 MOV AH,1
	 MOV AL,32
	 MOV BL,MOUSE4
	 INT 21

	 RET

MO1:	 MOV AL,29
	 INT 21
	 CMP AH,0
	 JG RET
	 MOV MOUSE1,AL

	 MOV AL,30
	 MOV AH,2
	 INT 21	 
	 CMP AH,0
	 JG RET
	 MOV MOUSE2,AL

	 MOV AL,31
	 MOV AH,2
	 INT 21	 
	 CMP AH,0
	 JG RET
	 MOV MOUSE3,AL

	 MOV AL,32
	 MOV AH,2
	 INT 21	 
	 CMP AH,0
	 JG RET
	 MOV MOUSE4,AL
	 
	 RET




HI:		PUSHF
		PUSH SI
		PUSH DI
		PUSH AX
		PUSH ES
		PUSH DX
		PUSH CX
		PUSH BX
		PUSH AX

		CALL VID_TEST
		MESSAGE DI,30,DOUC_MSG,10,25,0
		MOV BX,10000
		MOV CX,5
		POP AX		

HI1:		CALL HI2
		INC DI,2
		
		LOOP HI1
		POP BX
		POP CX
		POP DX
		POP ES
		POP AX
		POP DI
		POP SI
		POPF
		RET

HI2:		XOR DX,DX
		DIV BX  	;AX quotient,DX remainder
		ADD AL,48
		MOV AH,30
		ES MOV [DI],AX
		MOV AX,DX
		XOR DX,DX
		PUSH AX
		MOV AX,BX
		MOV BX,10
		DIV BX
		MOV BX,AX
		POP AX

		RET



; T2.TXT contains main control loop


;  START OF PROGRAM

START_HERE:     CALL SET_UP      ;call setup MPU initialisation routine
		CMP AX,0feh	;terminate : TSR (0feh) or MPU/screen file (0ffh) error
		JB  CON1
		jmp TERM	;terminate -no return

; >>>>>> MAIN CONTROL LOOP <<<<<<<<<


CON1:     CALL  CUREDIT       ;enter edit mode
          BLANK
          MOV BX,COMCUR       ;restore/set command cursor location

          MOV CPOS,BX

          CMP QUIT,1	    ;terminate
          JNE CON2

          MOV CPOS,3778       ;set cursor location for quit

CON2:     CALL COMPROC        ;command processor

          CMP QUIT,1          ;see if we are quiting

          JNE CON3           ;yes,to end program
	  MOV AX,0
	  jmp TERM

CON3:     MOV QUIT,0          ;reset quit flag

          MOV BX,CPOS         ;store command cursor location

          MOV COMCUR,BX

          JMP CON1            ;toggle to data entry mode


;>>>>>>>>> END OF MAIN CONTROL LOOP  <<<<<<<<<<<<<<

;  END OF PROGRAM


TERM:     	CMP AX,0feh
	  	JE TERM20			;TSR installation
	  	cmp ax,0ffh
	  	JE TERM25			;MPU/screen file error

term10:	BLANK
	  	INPUT 3840,30,ENDMSG,70,25,0,89,0,0
	  	CMP AL,89			;yes,quit now
	  	JE TERM30
	  	CMP AL,121
   	      JE TERM30

	  	MOV QUIT,0
	  	BLANK
	  	CALL co5			;restore command lines for data entry
	  	RET    				;don't quit

term20:	  	JMP TERM70	  
term25:		jmp term60

term30:	MOV AX,96			;put up closing splash
	  	CALL HELP
	        
term35:	mov cx,2			;wait 2 secs
		call intwait
		  
term40:	cmp mpu_flag,0ffh		;if no MIDI don't need to reset IRQ
	  	je term60

term50:	call reset_IRQ			;restore original IRQ vector

term60:	MOV AH,1
		CALL MOUSE
	  	CALL C_BRKRES

term70:	SCRES TSRBUF,25,0		;restore screen and border
		mov bl,0			;default border color
		MOV AH,0BH          		;colour palette function
	      MOV BH,0
     		INT 10H
	  	RET
	  	ENDP

; T21.TXT

;Subroutines to support initial set up and command selection:

	;VID_TEST,SET_UP,TSR_INST,COMPROC,COMDEF,RESCOM



;routine to test video mode and set ES address accordingly

VID_TEST: PUSH AX
	  MOV AX,0                      ;reset register
          MOV ES,AX
          CMP BYTE PTR ES:[449H],7      ;test video mode
          JE vid1
          MOV AX,0B800H                 ;colour buffer
          JMP vid2
vid1:      MOV AX,0B000H                 ;mono

vid2:      MOV ES,AX
	  POP AX
          RET


;Screen set up and MPU initialisation routines used on program entry

SET_UP:	SCRSAV TSRBUF,25,0    				;save screen before entry
		CSR 0,25	      				;zap cursor
       	MOV AX,0              				;screen index
            CALL HELP             				;set up entry screen
	      CMP AX,0ffh	      				;error handler
	      JE ret
      
set5:		mov ah,08
		int 21h		    				;wait for key
		

set10:	cs CMP TSRFLG,99					;see if TSR already installed
	  	je set20
	  	CALL TSR_INST					;check if user wants TSR and install if so
		cmp ax,0feh						;check for TSR installation error
		je ret
	  	

set20:	CALL C_BRK						;set up cntrl/brk handler
	  	MOV AH,2
	  	CALL MOUSE						;store mouse button settings and set defaults
	  

set30:	call setup_mpu     				;detect MPU and set port/IRQ, if no mpu, variable mpu_flag set to 0ffh
		cmp mpu_flag,0ffh
		je set50


set40:	call set_uart					;put MPU in UART mode
         	cmp mpu_flag,0ffh
		jne set55
	
set50:	INPUT 3840,124,NOMIDCD,80,25,0,0,0,0	;not OK terminate or continue without MIDI ?
	  	
set52:	CMP AL,27					;ESC to terminate
	  	JNE set70					;proceed without installing interrupt handler
	  	MOV AX,0ffh					;terminate on return
	  	ret

set55:	call MPUMSG					;initialise screen message
		MESSAGE 3860,31,MPUSETOK,59,25,0,0,0,0	;output the message
		mov cx,2					;wait 2 secs
		call intwait
		blank


set60:	call configureinterrupt			;configure interrupt and setup interrupt handler
          

set70:	BLANK
	  	MOV AX,99
          	CALL HELP           			;set up data entry screen
	  	CMP AX,0ffh					;error handler
	  	JE ret

          	MOV SI,offset HSBUF    			;read to entry screen buffer
          	MOV DI,offset OUT_BUF
          	MOV CX,4000
          
set80:     	MOV AL,B[SI]
          	MOV [DI],AL
          	INC SI
          	INC DI
          	LOOP set80


set90:      MOV DI,OFFSET INIT_BUF			;store initialisation parameters
          	CALL DIG1
	  	MOV AX,0					;reset error flag
	  	ret


;TSR installation control routine


TSR_INST:	mov ah,"T"	 	;search command line
		call search_tail
		cmp al,0ffh		;no command line or TSR setting found		
		je ret			;don't install
		cmp al,1		;for any setting other than T1 - no TSR required - exit
		jne ret


TS1:		CS CMP TSRFLG,99		;see if already installed
		JE TS2				;if so terminate 
  	 	CALL TSR
		cmp al,0feh			;installation error NB:not setup yet !!
		jne ret

TS2:		SCRES TSRBUF,25,0
		MESSAGE 1920,30,TSRINSE,36,25,0   ;TSR installation error
		ret


; Routine to set up and control I/O to command window
	
comproc:  MOV SI,3520              		;set source to start of command window
          MOV DI,OFFSET COMSC_BUF      	;buffer for storage of cmnd window
          MOV CX,240                    	;store 3 lines

COMP0:    ES MOV AX,[SI]               ;transfer a byte to buffer
          MOV [DI],AX
          INC SI,2
          INC DI,2
          LOOP COMP0
          MOV QUIT,0                    ;reset quit flag

;now read command menu to screen from buffer

          MOV SI,OFFSET LCOM_BUF         ;set to command buffer
          MOV AX,3520                   ;set to 23rd line
          MOV DI,AX

          MOV CX,240                    ;number of bytes to be written
          REP MOVSW                     ;write to screen

; routine to accept input data to overwrite any data at cursor location


CO0:      MESSAGE 3840,30,COMMSG,78,25,0
          MOV SI,CPOS          		;current screen position
          MOV CSIZE,10          		;set reset cursor size
          CURSOR 10,SI,124,SCBUF		;set cursor and store current contents

CO1:      MOV QUIT,1
          INPUT 3840,30,DY,1,25,0,0,0,0   ;get input
          CMP AL,"Q"          		;see if we are quiting
          JE co1A

          MOV QUIT,0   
	    CMP AH,59				;F1 pressed for help
	    JNE co1B
	    MOV AX,02
	    CALL HELP
	    JMP co1		

co1B:     CMP AL,27           ;see if escape was pressed (temp end)
          JE co1A

          CMP AL ,13          ;see if carraige return was pressed
          JE co4a

          CMP AL,32             ;see if space bar pressed
          JE co2A

          MOV SI, OFFSET CURLET         ;determine cursor location if single letter
          MOV CX,10

co2:      MOV BX,[SI]           ;get ASCII identifier
          CMP AL,BL
          JE co3
          INC SI,4
          LOOP co2


          BEEP
          JMP co1
co4a:	    JMP Co4
co1A:     JMP co5
co2A:     JMP co6

co3:      MOV AX,[SI+2]
          PUSH AX
          CURES
          POP AX
          MOV CPOS,AX
          MOV SI,CPOS     ;set cursor
          CURSOR 10,SI,124,SCBUF
          MOV AX,CPOS

co4:      CALL COMDEF         		;call command processor
	    CURES		     			;get rid of cursor at current location
          
	    CMP QUIT,1          		;see if we are quiting(1) or going into edit(2)
          JE RET

          CMP QUIT,2
          JE co5
          MOV AX,CPOS
          JMP co7              		;get next command

co5:      MOV AX,3520                   	;restore screen contents
          MOV DI,AX
          MOV SI,OFFSET COMSC_BUF
          MOV CX,240                    	;3 lines to restore
          REP MOVSW
          MOV CPOS,998
          RET                           ;return to main control loop

co6:      CURES

          MOV AX,CPOS
          ADD AX,20           		;and move to next position

co7:      CMP AX,3798         		;see if we are at last command
          JNE co8
          MOV AX,3558
          MOV CPOS,3558
          JMP CO0
co8:      CMP AX,3638
          JNE co9
          ADD AX,80
          MOV CPOS,AX
          JMP CO0


co9:      MOV CPOS,AX
          JMP CO0                       ;start input new data










;******************************** setup_mpu **************************
;
;
;		Routine searches for MPU at port 0300h or 0330h
;		if MPU responds, routine searches for IRQ to use
;
;		input :none
;		output: none, mpu_flag set to 0ffh if MPU not detected
;
;**********************************************************************

setup_mpu: 		mov int_flag,0						;reset interrupt flag
			mov PORT_MPU401,0330h         			;set base port address for MPU data
      		mov STATUSPORT_MPU401,0331h   			;port address for MPU status/control

smp10:		call ResetMPU401   		           		;attempt to reset MPU and put it in UART mode
      		cmp mpu_flag, 0feh            			;check all OK - if not mpu_flag will have been set to -1
      		je smp40
	
smp20:		mov PORT_MPU401,0300h         			;try alternative base port address for MPU data
      		mov STATUSPORT_MPU401,0301h   			;port address for MPU status/control

smp30:		call ResetMPU401   		           		;reset MPU and put it in UART mode
      		cmp mpu_flag, 0feh            			;check all OK - if not mpu_flag will have been set to -1
			je smp40							;can't detect the MPU

			mov PORT_MPU401,0378h         			;try printer port address for MPU data
      		mov STATUSPORT_MPU401,0379h   			;port address for MPU status/control

smp34:		call ResetMPU401   		           		;reset MPU and put it in UART mode
      		cmp mpu_flag, 0feh            			;check all OK - if not mpu_flag will have been set to -1
			je smp40							;can't detect the MPU
			mov mpu_flag,0ffh						;exit - error
			ret

smp40:		call get_IRQ					;scan for IRQ setting to use
			mov ah,0
			cmp al,0ffh
			jne smp50
			mov al,5					;IRQ setting not found - set default to IRQ5

smp50:  		mov wCtIntNum,ax
			ret

;*****************************  Get_IRQ ********************************
;
;
;		Routine searches the environment block
;		for the Blaster environment variable
;		and if it is found gets the current IRQ setting
;		if no blaster is present the routine then searches 
;		the program command tail for "H*" to indicate IRQ to use
;		if there is no command tail IRQ5 is used as default
;
;		two subroutines parse the blaster variable - check_blaster
;		and the command tail - search tail
;
;		input:none
;		output : IRQ in al or 0FFh if none found
;
;*************************************************************************


Get_IRQ:	push es
		push bx
		push cx

		mov bx,word ptr ds:[02CH] ; get current process's env segment
		mov es,bx
		mov si,0		    ;start at offset 0

geti10:     mov cx,0ffffh               ;block could be 64k long ?
	

geti20:		es mov al,[si]
		cmp al,"B"		;search for blaster
		jne geti30
		
geti25:		call check_blaster
		cmp al,0ffh		;either not blaster variable or IRq not found in variable
		jne geti100 		;found IRQ - exit
		es mov al,[si]		;got to continue so restore current byte

geti30:		cmp al,0		;see if end of block reached ( assume two 0's)
		jne geti40
		es mov al,[si+1]
		cmp al,0
		jne geti40
		jmp geti60		;end of block detected

geti40:		inc si
		loop geti20


geti60:		call search_tail	;nothing in environment block so search command tail

geti100:	pop cx
		pop bx
		pop es
		ret

check_blaster:  
                
		push cx
		push si


		es mov al,[si+1]
		cmp al,"L"
                jne cbl5

		es mov al,[si+2]
		cmp al,"A"
                jne cbl5

		es mov al,[si+3]
		cmp al,"S"
                jne cbl5

		es mov al,[si+4]
		cmp al,"T"
                jne cbl5

		es mov al,[si+5]
		cmp al,"E"
                jne cbl5

		es mov al,[si+6]
		cmp al,"R"
                je cbl10

cbl5:           mov al,0ffh
                jmp cbl50

cbl10:		mov cx,48		;maximum length of blaster settings		

		add si,8		;puts us at first byte of setting

cbl20:		es mov al,[si]
		cmp al,"I"		;prefix to IRQ
		jne cbl40

cbl30:		es mov al,[si+2]	;see if IRQ is above 9
		mov ah,48
		cmp al,32		;space indicates irq is between 1 and 9
		je cbl35
		mov ah,38

cbl35:          es mov al,[si+1]
		sub al,ah		;IRQ no is in al
		jmp cbl50


cbl40:		inc si
		loop cbl20
		mov al,0ffh		;couldn't find IRQ in blaster variable
		

cbl50:		pop si
		pop cx
       
		ret
;**************************************** search_tail *******************************************
;
;		Routine to search program command tail for a target setting
;
;
;		input:	ah, contains the ASCII value of the captial letter to search for
;		output: al contains the setting in range 1-19, or 0ffh if nothing found/no tail
;
;**********************************************************************************************	


search_tail:	push cx
		push si

		mov cl,byte ptr ds:[050H]	;get length of tail
		cmp cl,0
		jne st10
		mov al,0ffh			;nothing here				

st10:		mov si,051h			;puts at at first byte of tail

st15:		mov al,[si]
		cmp al,ah			;ah contains the ASCII value of the letter to search for in caps
		jne st50
		add ah,32
                cmp al,ah			;try lower case
		jne st50

st20:		mov al,[si+2]		;see if setting is above 9
		mov ah,48
		cmp al,32		;space indicates setting is between 1 and 9
		je st30
		mov ah,38		;if setting is 10-19 deduct 10 less from ASCII 0-9 value

st30:           mov al,[si+1]
		sub al,ah		;setting is in al
		cmp al,19		;check value is in range 1-19
		jg st55
		cmp al,0
		jb st55
		jmp st60

st50:		inc si
		loop st15

st55:		mov al,0ffh		;error setting not found

st60:		pop si
		pop cx
		ret

				
;**************************  Reset_IRQ ********************************
;
;
;		Routine restores IRQ vector on exit
;
;		input:none
;		output:none
;
;***********************************************************************


reset_IRQ:	push DS		;reset IRQ vector
		push dx
		push ax	

  		; restore interrupt
	
        	mov     al,byte ptr bSBC_INT_MASK_DATA      ; mask off the int
	        mov     dx,wIntMaskPort
        	out     dx,al

        	mov     bx,wSoftwareInt
        	mov     dx,ORG_INT_SEG
        	mov     ax,ORG_INT_OFS
        	call    SetIntVector
        	call    ResetMPU401

		pop ax
		pop dx
		POP DS
		ret

;*************************** IntWait ******************************
;
;
;		Waits for interval in secs
;
;		input: cx, number of seconds to wait
;		outout: none
;
;*******************************************************************


IntWait:	push ax
		push bx
		push dx   	


inw10:		push cx				;time limit in secs
		mov ax,0
		mov bx,0
		mov ah,02ch			;get system time at entry
		int 21h
		mov dl,0
		
		push dx				;dh has the second count at entry
	
inw20:      	pop bx				;retrieve the second count
		mov ah,02ch			;and check the time
		int 21h
		mov dl,0
		sub dx,bx			;subtract opening count from closing and store in dh
		jnb inw30
		add dx,60			;adjust for minute elapse

inw30:          pop cx
		cmp dh,cl                       ;set if we have reached time limit
		jae inw50
			
inw40:		push cx
		push bx				;store opening second count
		jmp inw20			;retry

inw50:		pop dx
		pop bx
		pop ax
		ret

;************************* MPUMSG *********************************
;
;		initialises string MPUSETOK with port and IRQ settings
;
;
;******************************************************************

mpumsg:	mov di,offset mpusetok
		cmp PORT_MPU401,0330h		;see if default address is being used
		je mpumsg50

mpumsg20:	add di,23
		mov al,"0"				;change 0330h to 0300h as only aternative address
		cs mov [di],al				;recognised

mpumsg50:	cmp wCtIntNum,5		;see if default IRQ5 is being used
		je ret

mpumsg80:	mov di,offset mpusetok
		add di,34				;get to string location
		mov ax,wCtIntNum		
		cmp ax,10
		jb mpumsg100
		mov ax,1				;IRQ must be 11 so adjust ax value

mpumsg100:	add ax,48				;convert to ascii
		cs mov [di],al
		cmp al,1
		jne ret
		mov [di+1],al			;print second char of "11"
		ret

		


;command processor routine

COMDEF:     MOV AX,CPOS            ;current cursor position
            CMP AX,3778            ;QUIT command selected
            JNE comd2
            MOV QUIT,1             ;set quit flag
            RET

;>>>INPUT<<<

comd2:      CMP AX,3558            ;input voice data from DX7
            JNE comd4
            CALL DXINP
		cmp ax,0			;return code 0 for all data read in,-1 if
		jne ret		 	;ESC pressed in file operation

            CMP NOVCS,1             ;if only single voice no need to shift
            JE comd3                  ;cursor

            MOV AX,3598             ;otherwise move it to read location
            MOV CPOS,AX

comd3:      CALL VCERED
            MOV QUIT,2             ;go into edit mode
            JMP comd8

;>>>OUTPUT<<<

comd4:      CMP AX,3578            ;output to DX
            JNE comd6
	    	cmp mpu_flag,0ffh
		je comd6
		CMP NOVCS,1
	    	je comd4a
	    	MOV OUTVC,2		    ;to give option of single or bank output
comd4A:     CALL OUTPSEL
	    	MOV OUTVC,1		    ;reset
            RET


;>>>READ<<<

comd6:      CMP AX,3598            ;load voice data from buffer of library
            JNE comd7
            CURES                  ;reset cursor
            CALL DIG               ;update buffer
            MOV QUIT,2             ;to put us in edit mode on return
            CALL VCERED            ;read in voice from buffer/file
            MOV CSIZE,5            ;reset cursor size
           

comd6a:	CMP QUIT,0             ;ESC or F10 pressed
            JE comd8
            RET             

;>>> WRITE <<<

comd7:      CMP AX,3618            ;save voice data in buffer to library
            JNE comd10
            MOV QUIT,0             ;to put us in command mode on return
            CALL DIG               ;update buffer
            CALL LIB               ;go to library & save
comd8:      CALL R1                ;restore screen
            CALL RESCOM            ;restore command window
            RET


;>>>>> PRINT <<<<<

comd10:    CMP AX,3718
           JNE comd11
           CALL PRINTV             ;routine to print voice(s)

;>>>>> PLAY  <<<<<

comd11:    CMP AX,3738
           JNE comd12
	   cmp mpu_flag,0ffh	   ;see if MPU is avilable
	   je comd12
           CALL OUTP0              ;put current voice in output buffer
           CALL PLAYV              ;routine to play chord sequence on DX

;>>>>> RANDOMISE  <<<<<<<<<<

comd12:    CMP AX,3758
           JNE RET
	   cmp mpu_flag,0ffh	   ;see if MPU is available
	   je ret
           CALL RANDV               ;routine to randomise voice parameters
           RET




; Routine to restore command window after save


RESCOM:     MOV AX,3520            ;start of window
            MOV DI,AX
            MOV SI,OFFSET LCOM_BUF
            MOV CX,240             ;3 lines
            CLD
            REP MOVSW

            RET



;Program T22.txt   Cursor data entry routines for main screen



CUREDIT:  MOV RCOL,144        ;reset column limits
          MOV LCOL,40
          MOV TROW,318
          MOV BROW,3200
          MOV CMOV,10
          MOV CPOS,998
          MOV CSIZE,5
          MOV QUIT,0          ;reset quit flag

CUR0:     MOV CX,CSIZE        ;cursor size
          MOV SI,CPOS
          CURSOR CSIZE,SI,124,SCBUF     ;set cursor and store current contents
          MOV CX,CSIZE
M0A:      MOV DI,CPOS
          ADD DI,CX           ;set to end of cursor
          ADD DI,CX
          DEC DI,2
          PUSH DI
          MESSAGE 3840,30,DENTRY,80,25,0
          POP DI


SM1:      MOV AH,01           ;function to determine keyboard status
          INT 16H             ;to BIOS
          JZ SM1               ;no input keep monitoring

          MOV AH,00           ;read waiting character
          INT 16H             ;to BIOS

	  CALL BL_CUR	      ;blank translation cursor values


          CMP AL,0            ;see if extended code was entered
          JE SM2

          CALL UNEXTENDED     ;unextended ASCII data handler
	  JMP SM3

SM2:	  CALL EXTENDED	      ;extended ASCII data handler
	  
SM3:	;check return codes

	CMP AX,99	;reset cursor
	JE CUR0
	CMP AX,98
	JE M0A

	JMP SM1


;T221.txt - deals with processing of unextended ASCII codes entered 
; on main data entry screen .Codes recognised and locations follow:

;	ESC (27) - SM1B
;	+/- (43/45) - CU16A
;	if CPOS below 211 then assume title entry .Routine is VOICE_TITLE
;	any other entry causes BEEP then return to control loop


UNEXTENDED:

SM1B:     CMP AL,27           ;see if ESC pressed
          JNE CU16            ;if it was toggle to command mode
          MOV QUIT,1
          MOV SI,CPOS
          CURES                       ;remove cursor
	  POP AX		;skip control -back to command processor
          RET                         ;return to main program


CU16:     CMP CPOS,210        ;see if we are entering voice title
          JA CU16A
	  CALL VOICE_TITLE	;returns direct to control loop

CU16A:	  CMP AL,45           ;subtract key pressec
          JE CU17
          CMP AL,43           ;addition key pressed
          JE CU17

	  BEEP			;invalid entry - recycle 
	  MOV AX,0		;return code
	  RET

CU17:     PUSH AX             ;store direction indicator
          ES MOV AL,[DI-6]    ;see if this is a double entry item
          CMP AL,""           ;if it is null then only single entry
          JE DENT3

          CMP LRIND,0         ;get flag to determine which data is to be incremented
          JE DENT3            ;1 is left ,0 is right
          
          MOV DX,6            ;set at units of left data
          ES MOV B[DI+1],124  ;set /reset background for right data to r/whte
          ES MOV B[DI-1],124
          CALL ADJUST	      ;amend data
          JMP DENT4


DENT3:    MOV DX,0
          ES MOV B[DI-5],124  ;set /reset background for right data to r/whte
          ES MOV B[DI-7],124  

          CALL ADJUST         ;amend data

DENT4:    CMP SOUND,1         ;see if sound toggle is set on
          JNE DENT4A           ;if not return
          PUSH DI
          CALL DIROUT         ;update voice from screen and output to DX
                              ;without updating main voice buffer
			      ;play note
				
          MESSAGE 3840,30,DENTRY,80,25,0

          POP DI
DENT4A:	  MOV AX,0		;return code
	  ret		



VOICE_TITLE:

CU18:     MOV CX,5           ;shift cursor contents right
          DEC CX
          MOV SI,CPOS

CUR3:     ES MOV BX,[SI+2]
          ES MOV [SI],BX
          INC SI,2
          LOOP CUR3


CUR4:     MOV AH,124           ;set attribute
          ES MOV [DI],AX     ;write to screen
	  POP AX
	  MOV AX,0		;return code
	  RET			;back to data entry control loop

;T222.txt -deals with extended ASCII codes on main data entry screen
;Routine deals with following ASCII 2 byte codes:


;	72 - Cursor Up		CU1
;	80 - Cursor Down	CU3
;	77 - Cursor Right	CU5
;	75 - Cursor Left	CU7


; Other Codes Handled Are :


;	68 - F10 - CU9  - End of data entry
;	67 - F9  - CU9E - Reset
;	66 - F8  - CU9A - Voice Init
;	83 - Del - CU9B - Restore from buffer
;	59 - F1  - CU9F - Help
;	60 - F2  - CU10 - Copy
;	61 - F3  - CU11 - Sound
;	62 - F4  - CU13 - Play
;	63 - F5  - CU14 - Right/left toggle

; All other codes are BEEPed at CU14. Also off limits cursor movement is
;detected and BEEPed at CU1A. Routine returns AX=0 for if cursor is not
;being moved on return, AX=99 if it is and AX=98 if data entry point is
; to be reset at current cursor location on return


EXTENDED:


;>>>>>> CURSOR MOVEMENT ROUTINE <<<<<<<<

          CMP AH,72           ;cursor up routine
          JNE CU3

CU1:      MOV DIR,1           ;direction indicator
          MOV AX,CPOS         ;current location
          CMP AX,TROW          ;see if its on the top line
          JG CU2

CU1A:     BEEP
	  MOV AX,0		;return code
	  ret
	  
CU2:      MOV BX,160          ;move cursor
CU2A:     CALL CURM
	  MOV AX,99		;return code
	  ret


CU3:      CMP AH,80           ;cursor down routine
          JNE CU5

          MOV DIR,2             ;direction indicator
          MOV AX,CPOS           ;current location
          CMP AX,BROW
          JNB CU1A              ;see if its on last line
	  JMP CU2

CU1B:	  JMP CU1A

CU5:      CMP AH,77           ;move cursor right
          JNE CU7

          MOV DIR,2           ;add to current offset
          MOV AX,CPOS         ;current location
          MOV DX,0
          DIV LINE            ;see where we are on current line
          MOV BX,RCOL
          CMP DX,RCOL          ;see if we are at limit of cursor mov.
          JNB CU1A              ;if not beep


CU6:      MOV BX,CMOV
          MOV AX,CPOS
          JMP CU2A



CU7:      CMP AH,75           ;move cursor left
          JNE CU9

          MOV DIR,1           ;subtract from current offset
          MOV AX,CPOS         ;current location
          MOV DX,0
          DIV LINE           ;see where we are on curent line
          CMP DX,LCOL        ;see if we are at left column limit
          JB CU1B              ;if not beep
	  JMP CU6


;>>>>> OTHER EXTENDED CODES<<<<<<<


CU9:      CMP AH,68           ;F10 pressed for end of entry
          JNE CU9E
          CURES
          MOV COMCUR,3558
	  POP AX		;skip control loop back to command processor
          RET

CU9E:     CMP AH,67           ;F9 pressed for reset voice
          JNE CU9A
          CALL DUMP
CU9C:     MOV CPOS,998
	  MOV AX,99		;set return code
          RET

CU9A:     CMP AH,66           ;F8 pressed for init voice
          JNE CU9B
          MOV SI,OFFSET INIT_BUF
          MOV VNUM,0
          CALL R1A	;******** << WATCH THIS ADDRESS<<<<<*****
          JMP CU9C

CU9B:     CMP AH,83           ;see if del was pressed
          JNE CU9F            ;if it was restore cursor contents from buffer
          MOV CX,CSIZE
          MOV DI,CPOS
          MOV SI,OFFSET SCBUF
          REP MOVSW
          MOV SI,CPOS
          CURES         ;reset cursor
	  MOV AX,99	;return code
          RET

CU9F:     CMP AH,59             ;F1 pressed for help
          JNE CU10
          MOV AX,1
          CALL HELP
CU9G:	  MOV AX,0
	  RET

CU10:     CMP AH,60           ;F2 pressed to copy envelope or operator
          JNE CU11
          CALL COPEO
	  MOV AX,98		;return code
	  RET

CU11:     CMP AH,61           ;F3 pressed to toggle sound on/off
          JNE CU13
	cmp mpu_flag,0ffh	;see if MPU available
	je CU9g
          MOV BX,3600
          CMP SOUND,1        ;test sound flag
          JE CU12

          ES MOV w[BX],07C4eH   ;"N"
          ES MOV w[BX+2],07C00H
          MOV SOUND,1
	  JMP CU9G

CU12:     ES MOV w[BX],07C46H  ;"F"
          ES MOV w[BX+2],07C46H  ;"F"
          MOV SOUND,0
          JMP CU9G

CU13:     CMP AH,62           ;F4 pressed to play current voice
          JNE CU14
	cmp mpu_flag,0ffh	;see if MPU available
	je CU9g
          PUSH DI
	  BLANK
          CALL OUTP0           ;output to DX
          CALL PLAYV          ;play the voice

CU13A:    BLANK
          MESSAGE 3840,30,DENTRY,80,25,0
          POP DI
	  JMP CU9G

CU14:     CMP AH,63           ;F5 pressed to toggle data entry to left or right
          JE CU14A
          BEEP
	  JMP CU9G

CU14A:    MOV BX,3624
          CMP LRIND,1        ;test flag
          JE CU15

          ES MOV w[BX],09e4cH   ;"L"
          MOV LRIND,1
	  JMP CU9G

CU15:     ES MOV w[BX],09e52H  ;"R"
          MOV LRIND,0
	  JMP CU9G


;T23.txt


;Routines to support data entry screen editing


;COPEO,BL_CUR,ADJUST,CUEND,CURM,SET_CAP,CUR_VAL,FREQ_DISP,TARPT1,VALTR


;Routine to copy envelope or operator to another location

COPEO:    BLANK
          MOV AX,CPOS
          MOV DX,0
          DIV LINE
          MOV AX,DX
          MOV DX,0
          SUB AX,38           ;6 operators occupy 20 bytes
          MOV BX,20
          DIV BX              ;AX now has op no in range 0-5
          MOV CX,5
          SUB CX,AX            ;invert to fit in with VTABL
          PUSH CX             ;store for later use

COP:      INPUT 3840,30,EOCOP,50,25,0,79,1,1  ;copy envelope or operator to ?

          CMP AL,13                      ;CR pressed for envelope
          JE COP1

          CMP AL,79                     ;<O> pressed for operator
          JE COP1

          BLANK                                 ;must be ESC
          POP CX
          RET

COP1:     PUSH AX             ;store command
          BLANK               ;get target operator
          MESSAGE 3840,30,EOINP,35,24,34  ;get target operator no

COP2:     MOV AH,01
          INT 21H

          CMP AL,49           ;make sure no is in range 1-6
          JB COP3
          CMP AL,54
          JBE COP4

COP3:     BEEP
          JMP COP2

COP4:     BLANK
;          CSR 0,25
          SUB AL,49           ;so first op is at offset zero
          MOV AH,0
          MOV CX,5            ;invert to fit in with VTABL
          SUB CX,AX
          MOV AX,CX
          MOV BX,42           ;21 bytes per operator so 42 coordinates
          MUL BX              ;AX now has offset in VTABL of start of
                              ;target operator
          POP DX              ;this is the command for voice/operator
          PUSH AX             ;store for later use
          CMP DL,13           ;envelope only
          JNE COP5
          MOV CX,8            ;set to first eight data elements of operator
          JMP COP6
COP5:     MOV CX,21           ;for whole operator

COP6:     MOV DI,OFFSET VTABL
          POP AX              ;offset of target address location in VTABL
          ADD DI,AX           ;DI is now at start of target operator


          MOV SI,OFFSET VTABL
          POP AX              ;number of source operator
          MOV DX,0
          MUL BX              ;multiply by 42
          ADD SI,AX           ;SI is now at start of source operator

COP7:     MOV BX,SI
          CALL CFF            ;to get source address
          ES MOV DL,[BX]      ;get the 'tens' part
          PUSH DX             ;store value
          ES MOV DL,[BX+2]    ;get the 'units' part
          PUSH DX             ;store value

          MOV BX,DI           ;to get target address
          CALL CFF
          POP DX              ;retrieve value
          ES MOV [BX+2],DL    ;write to target address

          POP DX              ;retrieve value
          ES MOV [BX],DL      ;write to target address
          INC SI,2
          INC DI,2

          LOOP COP7           ;get another data item
          RET


CFF:      MOV AL,[BX]         ;mov source row number to register
          DEC AL              ;adjust to end of previous row
          MUL ROW
          PUSH AX

          MOV AX,0
          MOV AL,B[BX+1]
          DEC AL
          CBW
          SHL AX,1
          POP BX
          ADD BX,AX           ;BX has absolute source offset
          RET


;Routine blanks translation cursor value

BL_CUR:		MOV BX,013b2H
		MOV SI,838
		MOV CX,60

BL_CUR0:	ES MOV [SI],BX
		INC SI,2
		LOOP BL_CUR0
		RET


ADJUST:   MOV  BX,DI
          SUB BX,DX
          ES MOV AL,[BX-2]    ;get tens byte
	  CMP AL,32	      ;see if it's blank
	  JA ADJ
	  MOV AL,0
	  JMP ADJ0

ADJ:      SUB AL,48           ;convert from ASCII
          MUL TEN

ADJ0:     ES ADD AL,[BX]      ;add units byte
          SUB AL,48           ;convert from ASCII
          PUSH AX          
          PUSH BX

          CALL SET_CAP        ;set the maximum value for location in BX reg

          POP BX
          POP AX
          POP SI              ;the return address
          POP DX              ;restore direction indicator
          PUSH SI             ;stack return address
          CMP DL,45           ;to subtract
          JNE ADJ3

          CMP AL,0            ;see if its already zero
          JG ADJ1
          MOV AL,CL
          JMP ADJ6
          
ADJ1:     DEC AL
          JMP ADJ6


ADJ3:     CMP AL,CL            ;increment value
          JB ADJ4
          MOV AL,0
          JMP ADJ6

ADJ4:     INC AL


ADJ6:     MOV AH,0            ;now divide to get tens and units

	  PUSH DI
	  PUSH SI
	  PUSH BX
          PUSH AX             ;store
	  CALL CUR_VAL	      ;display value in master block
	  POP AX
	  POP BX
	  POP SI
	  POP DI

ADJ6A:	  DIV TEN
	  PUSH AX          
          CMP AL,0            ;dont,print zero
          JE ADJ7
          ADD AL,48           ;convert to ASCII

ADJ7:     MOV AH,30
          ES MOV [BX-2],AX      ;write tens to screen
          POP AX
          MOV AL,AH
          MOV AH,30
          ADD AL,48           ;convert to ASCII
          ES MOV [BX],AX       ;write units to screen
          RET

;end of cursor routine

CUEND:         MOV SI,CPOS
               CURES                       ;remove cursor
               RET                         ;return to main program


CURM:     CURES
          MOV AX,CPOS
  
CM0:      MOV DL,DIR
          CMP DL,1            ;see if we are adding or subtracting
          JG CM1              ;if no is 2 we are adding
          SUB AX,BX           ;move up line or back column
          JMP CM2
CM1:      ADD AX,BX           ;mov down line or up column

CM2:      MOV CPOS,AX

;routine to check attribute of next cursor location and shift position
;counter if not a data location

          MOV SI,AX
          ES MOV AX,[SI]      ;get first character/attribute

          CMP AH,7            ;see if its a black space
          JE CM3
          CMP AH,48           ;see if its cyan
          JE CM3
          CMP AH,47           ;if green upper/lower limit reached
          JNE CM4
          CMP BX,160          ;check if only L/R move
          JE CM7              ;check if in bottom right corner
          CMP DL,1            ;see if moving right
          JE CM4
          CMP SI,2868
          JE CM9
          CMP SI,3188         ;line 20 moving right
          JE CM9
          CMP SI,3348         ;line 21 moving right
          JE CM9
          JMP CM4             ;otherwise skip to next location
CM9:      ADD SI,150

CM7:      BEEP
          MOV DL,DIR
          CMP DL,1
          JG CM5
          ADD SI,800
          JMP CM6

CM5:      SUB SI,160
          CMP SI,2958
          JNE CM6
          SUB SI,160

CM6:      MOV CPOS,SI
          JMP CM3

CM4:      MOV AX,SI
          JMP CM0            ;neither so move to next location

CM3:     RET


;Routine SET_CAP to define highest number for data entry


SET_CAP:  CMP CPOS,998        ;first eliminate non operator data
          JB SETC5
          CMP CPOS,3078
          JNB SETC5

;section deals with main operator datablock

          MOV CAP,0            ;reset cap variable
          MOV DX,1158          ;start of the second row
          MOV SI,OFFSET CAP_TABL        ;table of maximum data values

          MOV AX,CPOS         ;determine which half of operators data were at
          SUB AX,998          ;set to first cursor base
          DIV TWENTY          ;AH now has remainder
          CMP AH,0
          JE SETC1
          MOV AH,0            ;to read correct data from table
          MOV AL,2
          PUSH AX
          JMP SETC2

SETC1:    MOV AL,0
          PUSH AX             ;store

SETC2:    CMP CPOS,DX
          JB SETC3            ;find row location
          ADD DX,160          ;move to next row
          INC SI,4
          JMP SETC2

SETC3:    POP BX
          MOV AX,[SI+BX]      ;cap value taken from table
          ADD SI,BX           ;set cap variable
          MOV CAP,SI
          MOV CX,AX
          RET

SETC5:    MOV SI,OFFSET CAP_TABL        ;nonoperator cap values
          MOV AX,0
          MOV DX,0

SETC6:    MOV BX,[SI]
          CMP CPOS,BX
          JE SETC7
          INC SI,2
          JMP SETC6

SETC7:    MOV CX,[SI+2]       ;cap value set
          MOV CAP,SI          ;set cap variable
          ADD CAP,2
          RET


;Routine CUR_VAL to display DX equivalent of value at cursor location


CUR_VAL:  CMP CPOS,998        ;first eliminate non operator data
          JB CURC5
          CMP CPOS,3078
          JNB CURC5

;section deals with main operator datablock

          MOV DX,1158          ;start of the second row
          MOV SI,OFFSET DIS_TABL        ;table ofdisplay table indices

          MOV AX,CPOS         ;determine which half of operators data were at
          SUB AX,998          ;set to first cursor base
          DIV TWENTY          ;AH now has remainder
          CMP AH,0
          JE CURC1
          MOV AH,0            ;to read correct data from table
          MOV AL,2
          PUSH AX
          JMP CURC2

CURC1:    MOV AL,0
          PUSH AX             ;store

CURC2:    CMP CPOS,DX
          JB CURC3            ;find row location
          ADD DX,160          ;move to next row
          INC SI,4
          JMP CURC2

CURC3:    POP BX
          MOV AX,[SI+BX]      ;table number for display now in AL
	  JMP CURC8	      ;


CURC5:	  MOV SI,OFFSET DIS_TABL        ;nonoperator cap values
          MOV AX,0
          MOV DX,0

CURC6:    MOV BX,[SI]
          CMP CPOS,BX
          JE CURC7
          INC SI,2
          JMP CURC6

CURC7:    MOV AX,[SI+2]       ;display table number now in AL



CURC8:	  MOV SI,OFFSET ATABL1		;get the right table
	  CMP AX,1
	  JE CURC10
	  MOV SI,OFFSET ATABL2
	  CMP AX,2
	  JE CURC10
	  MOV SI,OFFSET ATABL3
	  CMP AX,3
	  JE CURC10
       	  MOV SI,OFFSET ATABL4
	  CMP AX,4
	  JE CURC10
	  MOV SI,OFFSET ATABL5
	  CMP AX,5
	  JE CURC10

;now see whether its frequency data to be output

CUE:	  CMP CPOS,1358		;before end of second data line (detune table1)
	  JA RET		;no translation necessary
	  CMP CPOS,320		;feedback and os ks
	  JB RET
	  POP DX
	  POP AX
	  PUSH AX
	  PUSH DX
	  CALL FREQ_DISP
	  RET


;now get value at cursor

CURC10:	 POP DX				;the return address
	 POP AX				;the target value
	 PUSH AX
	 PUSH DX 
	 PUSH SI
	 PUSH AX
       	 CALL TARPT1
	 POP AX
	 POP SI

	 MOV BL,B[SI]	;this gives number of letters required for value
	 MOV BH,0
	 INC SI		;skip over letter count
	 CMP CPOS,288	;key transpose table
	 JNE CURC11
	 ADD SI,59

CURC11:	 PUSH BX	;store for later use
	 MUL BL		;AX now has offset address for value

	 ADD SI,AX	;SI now has starting offset for display text

CURC12:	MOV DI,940	;the output address
	CALL VID_TEST
	POP CX		;number of characters to be output
	MOV AH,30

CURC13:	LODSB
        STOSW
	LOOP CURC13
	RET


FREQ_DISP:	PUSH AX
	
		CALL TARPT1
		
		MOV SI,CPOS		;get to data location
		ADD SI,8
		CMP CPOS,1120		;see which line we're on
	
	  	JA FR3

FR1:		MOV AX,CPOS		;find out which location we're at
		SUB AX,998
		DIV TWENTY
		CMP AH,0		;if AH>0 then we're at fine
		JNE FR2

		POP BX			;coarse
		ADD SI,10
		CALL VALTR
		MOV CX,AX
		ADD SI,150
		CALL VALTR
		MOV DX,AX
		JMP FR4

FR2:		POP CX			;fine
		SUB SI,10
		CALL VALTR
		MOV BX,AX
		ADD SI,160
		CALL VALTR
		MOV DX,AX
		JMP FR4

FR3:		POP DX			;mode
		SUB SI,150
		CALL VALTR
		MOV CX,AX
		SUB SI,10
		CALL VALTR
		MOV BX,AX

FR4:		MOV BH,0
		MOV CH,0
		MOV DH,0

		CMP DL,0		;see whether its ratio or fixed mode
		JE FR7

FR4A:		MOV AX,BX		;set coarse range
		MOV BL,4
		DIV BL
		MOV AL,AH
		MOV AH,0
		PUSH AX			;store

		MOV SI,OFFSET ATABL6	;get fine setting
		MOV AX,CX		;the fine setting
		MOV BL,4
		MUL BL			;AX now has offset
		ADD SI,AX

		POP DX			;range setting 0,1,2 or 3
		MOV BX,0
		MOV CX,4
		MOV DI,940
		MOV AH,30


FR5:		LODSB
		STOSW
	
    		CMP DX,BX
		JNE FR6
		CMP DX,3		;dont bother for 1000's
		JE FR6
		ES MOV W[DI],01e2eH	;gets decimal point in right place
	
		INC DI,2
FR6:		INC BX
        	LOOP FR5

		ES MOV W[DI],01e00H
		ES MOV W[DI+2],01e4BH	;print KHZ
		ES MOV W[DI+4],01e48H
		ES MOV W[DI+6],01e5aH
		RET

FR7:		CMP BX,0		;see if ratio is 0.5
		JNE FR8
		MOV AL,CL
		MOV BL,2
		DIV BL			;AL has remainder to be used
		MOV BH,AL
		ADD BH,50
		MOV AL,0		;tens
		MOV CX,0		;units
		MOV AH,30
		MOV DI,940
		JMP FR9

FR8:		MOV AL,CL		;multiply fine setting by coarse
		MUL BL
		MOV CL,100
		DIV CL			;AL now has units to add to coarse
					;AH has remainder after dec point
		ADD AL,BL		;gives total coarse value
		MOV BH,AH		;store remainder
		MOV AH,0
		DIV TEN			;AL has tens,AH units
		MOV CH,AH		;store units
		
		MOV AH,30
		MOV DI,940
		CMP AL,0
		JE FR9
		ADD AL,48
		STOSW			;puts tens on screen

FR9:		MOV AL,CH
		ADD AL,48
		STOSW			;puts units on screen
		MOV AL,"."
		ES MOV [DI],AX
		INC DI,2		;now print remainder

		MOV AL,BH
		MOV AH,0
		DIV TEN			;AL has first place,AH second
		MOV BH,AH
		MOV AH,30
		ADD AL,48
		STOSW
		MOV AL,BH
		ADD AL,48
		STOSW

FR10:		RET


TARPT1:	
	 PUSH AX
	 MESSAGE 838,96,CURTR1,26,25,0
	 POP AX
	 MOV BX,892
	 CALL ADJ6A
	 MESSAGE 894,96,CURTR2,23,25,0
	 RET



;Routine takes word at screen location SI,translates value from ASCII
;and puts result in AX

VALTR:		ES MOV AL,[SI]		;get units
		SUB AL,48
		CBW
		PUSH AX
		ES MOV AL,[SI-2]	;the tens
		CMP AL,47
		JA VALTR0
		POP AX
		RET

VALTR0:		MOV AH,0
		SUB AL,48
		MUL TEN
		POP DI
		ADD AX,DI
		RET




;T3.TXT  I/O ROUTINES   DXINP and DXSEND


;******************** DXINP ****************************
;
;		controls data input to main voice buffer
;		data can be read from file or the MPU
;
;		input:none
;		output:none
;
;*************************************************************
; Select file or DX input

DXINP:    	cmp mpu_flag,0ffh					;see if MPU is available
	    	je DXI15						;if not read from file

dxi10:	BLANK
	      INPUT 3840,30,SELMSG,76,25,0,70,1,0   	;read from file or DX
          	CMP AL,70           				;F for file 
          	JNE DXI20
	    	
dxi15:	call fileto_buf
		jmp dxi40

DXI20:    	CMP AL,27           				;ESC pressed to abort
          	jne dxi30
		mov ax,-1						;exit code
		ret

dxi30:	call mputo_buf
		cmp ax,0						;return code, -1 if no data
		jne dxi10						;recycle
	      
DXI40:    	MESSAGE 3840,30,EMSG ,37,25,0 ;display all data read in message
		
; transfer voice data to main buffer

DXI50:	call datato_buf
		mov ax,0						;exit code, 0 all OK
		ret

;******************** FILETO_BUF ****************************
;
;		gets a file name and calls readto_buffer
;		to read voice data to input buffer
;
;		input:none
;		output:none
;
;*************************************************************

fileto_buf:call get_filename
          ;MOV DX,OFFSET NAM
          ;MOV AH,10           			;get input
          ;INT 21H

          

ftb20:    BLANK
          CSR 0,25
          CALL readto_buffer			;call routine to read data
          CMP AX,99                     	;flag to retry
          JE fileto_buf
	    ret

;******************  get_filename ************************
;
;
;	checks user input for valid characters and stores
;	filename in buffer NAM
;
;	input:none
;	output:Al,0 if filename read successful,0ffh if user aborted
;
;**************************************************************

get_filename:	BLANK
          	MESSAGE 3842,31,FILEMSG,33,24,34

gf50:		mov cx,62				;initialise the filename buffer
		mov di,offset nam
		

gf100:	mov b[di],0
		inc di
		loop gf100

		mov di,offset nam
		mov cx,45				;maximum length of filename excluding CR
		mov [di],cl

gf150:	push cx

gf200:	mov ah,12				;clear keyboard buffer then get char without echo
		mov al,8
		int 21h				;then test for valid characters
		

gf250:	cmp al,13				;CR pressed for end of input
		jne gf350
		
gf300:	mov di,offset nam
		inc di
		mov al,45				;get number of characters in filename
		sub al,cl
		mov [di],al				;put total in second byte of buffer
		pop cx				;clean up stack
		mov al,0				;exit code 0
		ret

gf350:	cmp al,8				;backspace
		jne gf390
		pop cx				;get counter
		call backsp
		push cx				;store adjusted counter
		jmp gf200


gf390:	cmp al,27				;see if esc pressed
		jne gf450

gf400:	mov al,0ffh				;abort
		pop cx
		ret

gf450:	cmp al,46				;check for valid entry
		jb gf550
		cmp al,122
		jg gf550
				
gf500:	mov [di],al				;transfer valid char to buffer
		mov dl,al
		mov ah,2
		int 21h				;output to screen
		inc di
		jmp gf600

gf550:	beep					;invalid key entered

gf600:	pop cx
		loop gf150
		
gf700:	mov al,0				;end if line exit code 0
		ret


;***************** backsp **************************************
;
;
;		Positions cursor back one character,deletes
;		current entry, adjusts counter and filename buffer position
;
;		input:none
;		output:CX has adjusted counter value
;
;***************************************************************

backsp:		push di
		

bsp10:		cmp cx,45			;see if we're at beginning of buffer
		jb bsp50
		beep				; if we are just beep and exit
		pop di
		ret

bsp50:		push cx				;store counter
		

						;move back one space and send space to screen
						;then increment counter and recycle

bsp100:		mov ah,3			;get cursor position - DL has column no
		mov bh,0
		int 10h

bsp150:		dec dl				;move back one character
		mov ah,2
		int 10h				;reset cursor

bsp200:		mov ah,10			;write blank character at cursor
		mov al,32			;blank
		mov cx,1
		int 10h
		
bsp250:		pop cx
		inc cx				;adjust counter
		pop di				;get position in filename buffer
		dec di				;and adjust for deleted character
		ret





;******************  readto_buffer ************************
;
;
;	Routine reads 32 packed or up to 32 unpacked voices from
;	a file to the input buffer
;
;	input:none
;	output:??
;
;**************************************************************


readto_buffer: MOV AH,61           	;open DX data file for read
          	MOV AL,0            	;read only
          	MOV DX,OFFSET NAM   	;file title
          	ADD DX,2            	;jump over first two bytes (totals)
          	INT 21H
          	JNC rtb30             	;check for file error
        
rtb10:      BLANK
          	INPUT 3840,124,FNEXISTMSG,55,25,0,0,1,1   ;show error message
          
          	CMP AL,13             	;CR to retry
          	JNE rtb20
          	MOV AL,99             	;flag to retry
          	MOV AH,0
          	RET

          
rtb20:      POP AX                  ;go back to main command loop
          	POP AX
          	RET

rtb10a:	pop ax			;discard length of voice file
		jmp rtb10

rtb30:      MOV HANDLE,AX       	;store file handle
		mov ah,042h
		mov al,2		  	;move file pointer to end of file
		mov bx,handle
		mov cx,0
		mov dx,0
		int 21h
		jc rtb10		  

rtb35:	MOV VOICE,4096		;store length of file read (default)
		mov novcs,32
		MOV packed_flag,1	      ;indicates packed data for bank
		mov dx,0			;default file offset for read
		cmp ax,4096			;indicates file contains 32 packed voices without header
		je rtb100

rtb40:      cmp ax,4104			;indicates file contains 32 packed voices with header
		jne rtb50
		mov ax,4096
		mov dx,6			;data starts at byte 7 offset
		jmp rtb100
			

rtb50:	push ax			;file assumed to contain unpacked voices without header
		MOV BX,155			;ie 155 bytes per voice
          	DIV BX              	;determines number of banks
          	cmp ax,32
		jg rtb10a			;more than 32 voices
		cmp dx,0
		jne rtb10a			;not unpacked voice data
	  	mov novcs,ax
		MOV packed_flag,0		;indicates unpacked data	      
		pop ax			;get length of voice file
		MOV VOICE,ax		;store length of file read
		mov dx,0			;offset from beginning of file for read

		;now read data

rtb100:	MOV CX,ax          	;set for max. 32 voice read packed or unpacked
		push cx			;stack read count
          	mov ah,042h
		mov al,0		  	;move file pointer to beginning of file
		mov bx,handle
		mov cx,0			;msb of offset - lsb set above
		int 21h

rtb120:     pop cx			;get read count
		MOV AH,63           	;function call to read file
          	MOV BX,HANDLE       	;file handle
          	MOV DX,OFFSET HSBUF 	;offset of input buffer
          	INT 21H             	;to DOS


rtb140:     MOV AH,62           	;now close file
          	INT 21H
          	MOV AX,0            	;reset flag
          	RET                 	;all OK so return to main program





;******************** datato_buf ****************************
;
;		trasnfers voice data from input buffer HSBUF
;		to main voice buffer BUF
;
;		input:none
;		output:none
;
;*************************************************************


datato_buf:	BLANK
          	MOV CX,NOVCS        	;set number of voices
		CMP packed_flag,1	      ;see if bank needs unpacking
	  	JnE Dtb20
		MOV DI,OFFSET BUF   	;pointer to beginning of output buffer
		Mov DXINPUTFILE_OFFSET,0;offset   
          	CALL PUNK
          	JMP dtb80

Dtb20:    	MOV SI,OFFSET HSBUF 	;transfer data to main voice buffer
          	MOV DI,OFFSET BUF
	      MOV CX,Voice 		;<<<< multiple for file read as reqd.

Dtb40:    	MOV AL,[SI]         	;single voice read in
          	MOV [DI],AL
          	INC SI
          	INC DI
          	LOOP Dtb40

Dtb80:    	MESSAGE 3840,30,TMSG,37,25,0 ;display finished message
	  	
		mov cx,2
		call intwait

          	MOV DX,0
          	MOV AX,0
          	RET                 ;all data read in return to command processor

;******************** mputo_buf ****************************
;
;		gets voice data from DX and puts it in HSBUF
;		timer routine is used whilst waiting for IRQ call
;
;		input:none
;		output:AX,0 if data read in OK, -1 if no data and user want to continue without
;
;*************************************************************

mputo_buf:	BLANK

mputb2:	INPUT 3840,30,INMSG2,54,25,0,0,1,1	;tell user to set upDX for MIDI send                
          	
mputb5:	MESSAGE 3840,30,MSG,56,25,0
          	mov int_flag,1

mputb10:   	mov ax,0
		mov bx,0
		mov ah,02ch				;get system time at entry
		int 21h
		mov dl,0
		push dx				;dh has the second count at entry
	
mputb20:    pop bx				;retrieve the second count
		mov ah,02ch				;and check the time
		int 21h
		mov dl,0
		sub dx,bx				;subtract opening count from closing and store in dh
		cmp dx,0
		jnb mputb30
		add dx,60				;adjust for minute elapse

mputb30:    cmp dh,10                     ;allow 10 seconds for user to setup DX for output
		jb mputb40
		mov int_flag,0			;timeout
		jmp mputb50
	
mputb40:	cmp int_flag,0			;all data read in OK
		jne mputb45
		mov ax,0				;return code 0 for all OK
		ret
	
mputb45:	push bx				;store opening second count
		jmp mputb20				;retry

		;timeout

mputb50:    MESSAGE 3840,116,IRQMSG,56,25,0
	     	MOV AH,8            		;get input
           	INT 21H

           	CMP AL,13           		;see if we are continuing
           	jne mputb60
		mov ax,-1				;exit code -no data continue without input
		ret

mputb60:    CMP AL,27           		;see if we are trying again
          	JNE mputb70          		
		BLANK
          	JMP mputo_buf

mputb70:    BEEP
          	JMP mputb50             		;incorrect entry,recycle







;DX output routine

; send message to screen and set counter

DXOUT:    	cmp mpu_flag,0ffh		;see if MPU is available
		je ret				;if not exit
		BLANK
          MESSAGE 3840,30,OMSG,22,25,0
         

XO0:      MOV SI,OFFSET BUF
          MOV DI,OFFSET HSBUF
          MOV VOICE,155       ;default byte count
          CMP OUTVC,32        ;see whether 1 or 32 voices to be sent
          JE XO2

          CMP NOVCS,1         ;see whether only 1 voice in buffer
          JE XO1

          MOV AX,155         ;see which single voice to be transmitted
          MUL VNUM
          ADD SI,AX           ;reposition source offset

XO1:      MOV CX,155

XO1A:	  MOV AL,[SI]
	  MOV [DI],AL
	  INC SI
	  INC DI
	  LOOP XO1A

          JMP DXSEND

XO2:      MOV VOICE,4096		;initialise buffer
          MOV CX,5000
          MOV AX,0

XO3:      MOV B[DI],AL
          INC DI
          LOOP XO3
	   	MOV DI,OFFSET HSBUF	;reset to beginning of output buffer          
		CALL PACK


;Routine to send 1 or 32 voices to DX

DXSEND:   BLANK
          MESSAGE 3896,30,DOWNBK,24,25,0
          CHECKSUM HSBUF,VOICE,4002,4004,DXS10


DXS10 :   MOV AX,0
          MOV SI,OFFSET HSBUF ;read from output buffer
          MOV AL,OUTVC        ;see whether 1 or 32 voice transfer reqd
          CMP AL,1
          JE DXS20

          MOV form1,9
          MOV form2,0
          JMP DXS40

DXS20:    MOV form1,0
          MOV form2,27

;output section

DXS40: 	call dx_out
	  	cmp mpu_flag,0		;all OK
	  	je DXS60			;exit

DXS50:    	MESSAGE 3840,116,IRQMSG,56,25,0
	  	mov mpu_flag,0
          	ret			


DXS60:	BLANK
          	MESSAGE 3840,30,ALSENT,11,25,0
          	RET




;***************************  dx_out *****************************
;
;
;			SYSEX OUTPUT HANDLER
;
;******************************************************************



dx_out:   	sub cx,cx		;maximum of 65536 tries

dx_out10: 	call is_output		;check we can send data now     
        	jz dx_out20
		loop dx_out10
		mov mpu_flag,0ffh	;set error flag
		ret			;can't send data right now

dx_out20:	mov al,240		;status byte
		call writeMPU401
		mov al,67		;ID number
		call writeMPU401
		mov al,0		;sub status and channel number
		call writeMPU401
		mov al,form1		;identifier for sending 1 or 32 voices
		call writeMPU401
dx_out40:	mov al,outvc		;no of voices to be sent
		call writeMPU401
		mov al,form2		;byte count
		call writeMPU401

dx_out60:	mov cx,voice
		MOV SI,OFFSET HSBUF

;Send voice data proper

dx_out80:   PUSH CX
	  	MOV AL,[SI]
         	call writeMPU401
	      INC SI
	  	POP CX
		cmp outvc,32
		jne dx_out90
		call spt_voiceno

dx_out90:   LOOP dx_out80
	  

dx_out100:	mov al,CHECK          	;checksum byte
		call writeMPU401
          	mov al,247		;send end of exclusive
		call writeMPU401

dxout_120:	ret


;********************** spt_voiceno ***************
;
;	calculates voice number in 32 voice download
;	and calls    to print to screen
;
;	input:none
;	output:none
;
;**************************************************


spt_voiceno:	pusha
			MOV ax,cx
			mov bl,128
			div bl
			cmp ah,0
			jne sptv100

sptv50:		not al				;convert to 0-32 sequence
			sub al,222
			mov di,3946
			call scrpt_ascii			;display voice number


sptv100:		popa
			ret	






;T31.txt 

;Main I/O  support routines


;set_IRQhandler,ConfigureInterrupt,SetIntVector,GetIntVector
;MIDIISR,IPUT,OUTPUT,SEND_COMMAND,OUTPSEL,
;PLAYV,AUDITION,DIROUT

;Routine to set up IRQ handler

set_IRQhandler:    



        ; -- setup interrupt service routine

        cli
        mov     bx,wSoftwareInt             	; preserve original ISR
        call    GetIntVector                	;
        mov     ORG_INT_OFS,AX              	;
        mov     ORG_INT_SEG,DX              	;

        mov     bx,wSoftwareInt             	;setup new ISR
        mov     dx,cs                       	;
        mov     ax,offset midiisr       	;
        call    SetIntVector                	;

        ; -- enable interrupt mask

        mov     dx,wIntMaskPort
        in      al,dx                           ;preserve it
        mov     byte ptr bSBC_INT_MASK_DATA,al
        mov     ah,byte ptr bIntMaskBit
        not     ah
        and     al,ah
        out     dx,al


	ret



;;-----------------------------------------------------------------------
;;  SetIntVector
;;      Set interrupt vector.
;;  Entry :
;;      bx      = vector number.
;;      dx:ax   = new vector.
;;  Exit:
;;      none.
;;-----------------------------------------------------------------------

SetIntVector :

        pushf
        push    ds

        shl     bx,1
        shl     bx,1

        cli

        push    ax
        sub     ax,ax
        mov     ds,ax
        pop     ax

        mov     [bx],ax
        mov     [bx+2],dx

        pop     ds
        popf

        ret

;;-----------------------------------------------------------------------
;;  GetIntVector
;;      Get interrupt vector.
;;  Entry :
;;      bx      = vector number.
;;  Exit:
;;      dx:ax   = current vector.
;;-----------------------------------------------------------------------

GetIntVector:

    pushf
    push    ds

    shl     bx,1                        ; 4 byte per vector
    shl     bx,1

    cli
    sub     ax,ax                       ; vector table at segment 0
    mov     ds,ax

    mov     ax,[bx]
    mov     dx,[bx+2]

    pop     ds
    popf

    ret


;;-----------------------------------------------------------------------
;;  ConfigureInterrupt
;
;;      Configure interrupt parameters based on wCtIntNum.
;	and set up IRQ handler
;
;;  Entry :
;;      none.
;;  Exit:
;;      none.
;;-----------------------------------------------------------------------

ConfigureInterrupt  :

        push    ax
        push    cx

        mov     ax,wCtIntNum
        and     al,0FH

        mov     cx,ax

        and     cl,7H
        mov     ch,1
        shl     ch,cl
        mov     byte ptr bIntMaskBit,ch

        mov     cl,al
        add     cl,8
        cmp     cl,10H
        jb      ASI10

        add     cl,60H

ASI10:
        sub     ch,ch
        mov     wSoftwareInt,cx
        and     al,8
        cbw
        mov     cl,4
        shl     ax,cl
        add     ax,21H
        mov     wIntMaskPort,ax

        pop     cx
        pop     ax


 ; -- setup interrupt service routine

        cli
        mov     bx,wSoftwareInt             ; preserve original ISR
        call    GetIntVector                ;
        mov     ORG_INT_OFS,AX              ;
        mov     ORG_INT_SEG,DX              ;

        mov     bx,wSoftwareInt             ;setup new ISR
        mov     dx,cs                       ;
        mov     ax,offset MidiISR           ;
        call    SetIntVector                ;

        ; -- enable interrupt mask

        mov     dx,wIntMaskPort
        in      al,dx                           ;preserve it
        mov     byte ptr bSBC_INT_MASK_DATA,al
        mov     ah,byte ptr bIntMaskBit
        not     ah
        and     al,ah
        out     dx,al



        ret


;;-----------------------------------------------------------------------
;;  WriteMPU401
;;      Write a byte of MIDI data to MPU-401
;;  Entry :
;;      al      = output data.
;;  Exit :
;;      none.
;;-----------------------------------------------------------------------

WriteMPU401:

        push    dx

        mov     dx,PORT_MPU401      ;base i/o address
        inc     dx                  ;status port
        push    ax
Busy3:
        in      al,dx               ;read status port
        test    al,40h              ;ready for output ?
        jnz     Busy3               ;Nope

        dec     dx                  ;data port
        pop     ax                  ;output data
        out     dx,al               ; via data port

        pop     dx
        ret

;**************************************************************



;****************************** MIDIISR ************************************** 

;             IRQ HANDLER
;
;	MPU interrupt handler
;	services all call from MPU
;
;
;****************************************************************************


MIDIISR:   


 	  push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    ds
        pushf

        mov     ax,cs
        mov     ds,ax

IRQH_20: mov     dx,PORT_MPU401     	;base i/o address

AGAIN:
        inc     dx                  	;status port
        in      al,dx               	;read status port
        test    al,80H              	;input data available ?
        jnz     NO_DATA             	;no more data

        dec     dx                  	;data port
        in      al,dx               	;input data via data port

	pusha			    	;preserve machine state
	pushf
        call    iput          	    	;decide what to do with data
	popf
	popa
       
        jmp     AGAIN			;see if there's more data waiting

NO_DATA:
        mov     al,20H
        cmp     word ptr ds:wCtIntNum,8     ; high interrupt ?
        jb      II90                        ; no, skip next

        out     0A0H,al                     ; yes, acknowledge slave controller
II90:
        out     20H,al                      ; acknowledge master controller

        popf
        pop     ds
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        iret


;*************************** iput ******************************
;
;		SYSEX INPUT HANDLER
;
;	entry AL - data byte from MPU
;
;	exit - none
;
;	uses int_flag for MIDI status and to define position
;	in sysex
;
;****************************************************************

iput:	  


	cmp int_flag,1			;see if we're waiting for or reading a sysex
	jae inp10				;no, exit
	;mov ah,02f
	;SCREENPT 3980,ax			;signal non sysex data received
	ret

inp10:cmp int_flag,7			;see if we're reading to buffer
	jne inp30
	
	;Main sysex read section

	push ax
	mov bx,sysex_count		;get buffer location address counter
	mov di,bx
	mov [di],al         		;transfer byte to the buffer
     	inc di              		;increment buffer address
	mov bx,di
	mov sysex_count,bx		;store it in counter
	mov ah,02fh
	SCREENPT 3988,AX
	pop ax

inp15:cmp al,0f7h				;check for end of sysex
	jne ret
	
inp20:mov int_flag,0			;eos,exit reset flag
	mov header_count,0		;reset header counter
	mov sysex_count,0
	SCREENPT 3988,02f34H
	ret

	;Header read section

inp30:cmp header_count,2		;byte 2 is Yamaha identifier
	jne inp40

	;check it's a yam sysex

	cmp al,67				;check for Yamaha identifier
	je inp35
	mov int_flag,1			;reset flag to waiting for sysex
	mov header_count,0		;reset header count
	ret

inp35:SCREENPT 3984,02F32H
	inc header_count			;increment the header counter
	ret

inp40:cmp header_count,4		;4th byte contains identifier for length of sysex
	jne inp50
	mov packed_Flag,1			;flag to indicate data will require unpacking
	cmp al,9				;check we have either a voice or bank sysex
	je inp45
	mov packed_flag,0			;unpacked data
	cmp al,0
	je inp45
	mov int_flag,1			;reset flag to waiting for sysex
	mov header_count,0		;reset header count
	ret
	
inp45:mov header_count,5
	MOV VOICE, 155			;store dump length
	Mov novcs,1				;single voice input
	cmp al,9				;for packed 32 voice sysex dump
	jne ret
	MOV VOICE, 4096
	mov novcs,32			;32 voice input
	ret

inp50:cmp header_count,6		;last byte of header
	jne inp60
	mov int_flag,7			;set flag for start of sysex read
	MOV BX,OFFSET HSBUF 		;starting address of input buffer
	MOV SYSEX_COUNT,BX		;initialise sysex buffer address counter
	SCREENPT 3986,02F33H 		;moving on to read main voice data
	ret

inp60:cmp al,240				;see if the byte is start of sysex
	jne inp80				; no, so recycle
	mov int_flag,2
	mov header_count,2		;initialise header counter
	SCREENPT 3982,02F31H		;signal start of sysex read
	ret

inp80:cmp int_flag,2			;check to see if we are reading header
	jb ret				;if we are simply increment header count as
	inc header_count			;bytes 3 and 5 of header are not needed
	ret







; NEW MIDI ROUTINES


;************************** is_input() ****************************

; Checks if there is a byte waiting to be read from the MPU. Clears
; the Z flag if so. Sets the Z flag if not.
;
; Uses registers AL DX

is_input:    

 	; Return state of DATA SET READY. MPU clears this line when it    
 	; has a byte waiting to be read from its DATA port.     

	mov dx,STATUSPORT_MPU401
	in al,dx     
	and al,80h     
	ret


;************************** get_mpu_in() ***************************

; Reads a byte from the MPU's DATA port. Returns the byte in AL.
;
; Uses register DX

get_mpu_in: 

     	mov dx,PORT_MPU401
     	in al,dx     
	ret

;************************** is_output() ****************************

; Checks if it's OK to write a byte to the MPU's COMMAND or DATA ports.
; Clears the Z flag if so. Sets the Z flag if not.
;
; Uses registers AL DX

is_output:

	;---Return state of DATA READ READY. MPU clears this line when 
	; it's OK to write to the MPU's ports.     

	mov   dx,STATUSPORT_MPU401    
	in    al,dx     
	and al,40h     
	ret

;************************* put_mpu_out() ***************************
; Writes a byte to the MPU's DATA port. The byte is passed in AL
;
; Uses register DX

put_mpu_out:

	mov   dx,PORT_MPU401
	out dx,al     
	ret

;************************** ResetMPU401() ****************************

; Sets the MPU into Uart mode. Called before interrupt handler is setup
;
; Uses registers AL DX



	;---Wait until it's OK to write to the MPU's ports.

ResetMPU401:  sub cx,cx             ;set counter

sr10: 	call  is_output     
	jz   sr15
        loop sr10
	mov mpu_flag,0ffh	;set error flag
	ret			; can't write to MPU    

 	;---Send power up reset command to the MPU.     

sr15:	mov al,0FFh     
	out dx,al     

	;---Wait for the MPU to make a byte available for reading from its  DATA port.

	sub cx,cx		;maximum of 65536 tries

sr20: 	call is_input     
        jnz sr28

	;---Get the byte and check for an ACK (FE) to the cmd we sent. If not an ACK,
	; discard this and keep looking for that ACK.     

sr25:	call  get_mpu_in     

	cmp al,0FEh
        jne sr28
        mov mpu_flag,al
        ret

sr28:	loop sr20
	mov mpu_flag,0ffh	;set error flag
	ret			;no ACK received so no MPU out there !

 ;********************** set_uart *************************
 ;
 ;                      Puts MPU in UART Mode
 ;
 ;*********************************************************
	;---Wait until it's OK to write to the MPU's ports.


set_uart:   sub cx,cx               ;maximum of 65536 tries

sr40:	call  is_output     
        jz SR50
	loop sr40
	mov mpu_flag,0ffh	;set error flag
        ret                     ;can't write to MPU

sr50:   mov al,03Fh
	out dx,al 
	sub cx,cx		;maximum of 65536 tries
	
sr60:	call is_input
	jnz sr80

	;get the byte and check it is an ACK    

sr70:	call get_mpu_in
	cmp al,0feh
	je sr90

sr80:	loop sr60
	mov mpu_flag,0ffh	;set error flag
	ret			;no ACK received when setting UART mode

sr90:	mov mpu_flag,0		;all OK reset mpu_flag
	ret




; END OF NEW MIDI ROUTINES






;
;             Send command to MPU401
;
;
;
;______________________________________________________________________________


Send_Command : 
                
		push ax
		push dx
		push cx

sc10:		mov  dx,STATUSPORT_MPU401          		; MPU status port		
		mov cx,50000

sc20:		in   al,dx               	; read status
              	test al,64              	; wait for bit 6 = 0
              	jz  sc40
	      	loop sc20

sc30:	      	mov mpu_flag,5			;can't proceed now - exit
	     	jmp sc80

sc40:          	cli
	      	mov  al,mpucmnd                 ; Retrieve command
              	out dx,al               	; Send command
		mov dx,STATUSPORT_MPU401			; read STATUSPORT

		
	        mov cx,50000

sc50:		in   al,dx			;get MPU to acknowledge
	        test al,dsr
              	jz  sc60
	      	loop sc50
	      	jmp sc80			;failed to acknowledge - exit	      

sc60:          	mov  dx,PORT_MPU401
              	in al,dx
	      	mov mpu_flag,al			; if all OK this should be 0ffh

sc80:          	pop cx
		pop dx
		pop ax
	      	sti
		ret




;Routine to determine whether to send 1 or 32 voices to DX and output

OUTPSEL:    CMP NOVCS,0            ;see if buffer is initialised
            JG OUTP0

            INPUT 3840,79,VCMSG,63,24,62,0,1,1   ;display error message
            CMP AL,13
	    JE OUTP0
            RET

OUTP:	    JMP OUTPSEL

OUTP0:      CALL DIG               ;update output buffer from screen
      
OUTP0A:     CMP NOVCS,2            ;see if we only have one voice in buffer
            JB OUTP4
            CMP OUTVC,2            ;set to 2 for bank play poss. only
	    jne outp4
	    mov outvc,1		   ;set default to single voice output

            INPUT 3840,30,OUTSEL,48,25,0,66,1,1   ;display selection mesage

            CMP AL,27              ;see if escape pressed
            JNE OUTP2
            POP AX
            RET

OUTP2:      CMP AL,13              ;see if current voice to be sent
            JE OUTP4


            CMP AL,66              ;32 voice transfer required
            JNE OUTP            ;error on input try again
            MOV OUTVC,32           ;bank to be sent

OUTP4:      BLANK
            CALL DXOUT             ;output voice data to DX
	    cmp mpu_flag,0	   ; check for output error
	    je ret
            jmp outp		   ;recycle


;Routine to send chord sequence to DX


PLAYV:	

PLY10:     CALL SETRND         ;use random number table to define start chord
          MOV SI,RANST
          MOV AL,B[SI]
          MUL TEN
          ADD AL,B[SI+1]
          MOV AH,0
          CMP AL,48
          JB PLY10
          CMP AL,72
          JA PLY10

ply20:    MOV NOTE,AL	  

PLY30:  call play_chorda
	mov bh,0
	call play_chordb
	call play_chorda
	mov bh,0ffh		;to signal only 2 notes in final chord
	call play_chordb	
	  RET

play_chorda:

	push bx
	mov bl,note
        mov ch,85                       ;note velocity
	CALL NOTE_ON 
       
	ADD bl,4
	mov ch,45
	CALL NOTE_ON 
        dec bx,2
	mov ch,55
	call NOTE_ON 
      	mov ch,45
	ADD bl,3
	call NOTE_ON 

	dec bl
	mov note,bl

	pop bx
	ret


play_chordb:

	push bx
	mov bl,note
        mov ch,75               ;note velocity
	CALL NOTE_ON 
       
	ADD bl,3
	mov ch,50
	CALL NOTE_ON 
        dec bl,2

pcb40:	cmp bh,0ffh		;see if we are on the final chord
        jne pcb50
	mov ch,70		;slight emphasis on last note
	call note_on
	jmp pcb60		

pcb50:	mov ch,60
	call NOTE_ON       
	ADD bl,4
	mov ch,4
	call NOTE_ON 

	dec bl
	dec bl
	mov note,bl

pcb60: ; mov cx,1
       ; mov dx,0250h             ;1.5sec delay for note on
       ; call pldel

	pop bx
	ret


;**************************** note_on *****************************
;
;		sends a single note on/off to MIDI
;
;
;		input:	note in bl, eg 60 =C3
;		output:	none
;
;*****************************************************************


note_on:        push ax
                push bx
                push cx
                push dx

note10:         
		call send_note		  ;play note
                mov cx,1
                mov dx,0500h             ;1.5sec delay for note on
		call pldel

		mov ch,0			;velocity
		call send_note			;swith note off
          
                pop dx
                pop cx
                pop bx
                pop ax
		ret
		
send_note:	push cx
		MOV AL,144		;channel message for note on
		call writempu401
		mov cx,0
                mov dx,50000             ;1.5sec delay for note on
                call pldel

note20:		mov al,bl
		call writempu401
		mov cx,0
                mov dx,50000             ;1.5sec delay for note on
		call pldel	
		
note30:	  	pop cx
		MOV AL,ch		;send velocity
                call writempu401
                ret


;Routine Audition to output current voice to DX

AUDITION:	PUSH AX		     ;store key indicator
		MOV BIND,98	     ;only single voice output
		CALL VCE_ID	     ;get voice location
	        CALL L40	     ;read data to main voice buffer


          	MOV DI,OFFSET RAND_BUF               ;offset for temporary buffer
          	MOV CX,80                      ;cursor size
	  	MOV SI,3840

AUD1:     	ES MOV AX,[SI]      ;put character and attribute in buffer
          	MOV [DI],AX         ;store them
          	INC SI,2            ;next character
          	INC DI,2
          	LOOP AUD1

		MOV OUTVC,1	     ;single voice output
		MOV VNUM,0
		CALL DXOUT           ;output to DX
		MOV BIND,0


          	MOV SI,OFFSET RAND_BUF               ;offset for temporary buffer
          	MOV CX,80                      ;cursor size
	  	MOV DI,3840
	  	REP MOVSW

		
		POP AX
		CMP AH,82
		JE RET
	        CALL PLAYV
		RET



;Routine DIROUT to update output buffer from screen and send to DX


DIROUT:   MOV DI,OFFSET HSBUF   ;read to output buffer
          MOV CX,145
          PUSH DI             ;store starting offset
          MOV SI,OFFSET VTABL ;table to translate screen positions

DIR0:     MOV AX,0
          MOV AL,[SI]         ;move row number to register
          DEC AL              ;adjust to end of previous row
          MUL ROW
          PUSH AX


          MOV AX,0
          MOV AL,B[SI+1]
          DEC AL
          CBW
          SHL AX,1
          POP BX
          ADD BX,AX           ;BX has absolute source offset
          PUSH BX             ;store the location

          ES MOV DL,[BX]      ;get the 'tens' part
          CALL ZECHK
          MOV AH,0
          MOV AL,DL
          SUB AL,48           ;convert from ASCII
          MUL TEN             ;multiply by ten
          MOV AH,0

          POP BX              ;restore location
          ES MOV DL,[BX+2]    ;get the 'units' part
          CALL ZECHK
          MOV AH,0
          SUB DL,48           ;convert from ASCIIa
          ADD AL,DL           ;this gives the actual number

          MOV [DI],AL         ;update buffer
          INC DI
          INC SI,2

          LOOP DIR0

          POP DI              ;restore starting offset for current voice
          MOV AX,198          ;starting offset of voice name on screen
          MOV SI,AX           ;source register
          MOV BX,145
          MOV CX,10

DIR1:     ES MOV AL,[SI]
          MOV [DI+BX],AL
          INC DI
          INC SI,2
          LOOP DIR1

	  MOV OUTVC,1
	  CALL DXSEND
	  
	  mov bl,60
          mov ch,75               ;note velocity
	  CALL NOTE_ON 

          RET

;T32.txt


;   Contains routines supporting main I/O:

;	PUNK,PACK,BANKPRT

;Routine to unpack bytes and place resolved bits in output buffer
;begin by setting up main voice loop

;		input: DI has address of output buffer

PUNK:     MOV CX,32           ;number of voices for main loop
          MOV COUNT_V1,0      ;reset counters
          MOV COUNT_V2,0      ;reset counters
          MOV COUNT_V3,0      ;reset counters

          

VCS:      PUSH CX        ;store main counter
          MOV CX,6       ;each voice has 6 envelopes of 21 unpacked
                         ;bytes and 29 bytes of non envelope data

ENV:      PUSH CX                  ;store envelope counter
          MOV CX,21                ;reset counter for 21 bytes of voice data
          MOV SI,OFFSET ENV_DATA   ;source pointer to I/O translator
          MOV BTABL,OFFSET UNP_ENV ;indicates table to use
          CALL UPDAT               ;read data

          ADD COUNT_V2,17     ;increment envelope counter
          POP CX              ;restore number of envelopes to counter
          LOOP ENV            ;read another envelope

NENV:     MOV CX,29                ;counter reset for rest of voice data
          MOV SI,OFFSET NON_ENV    ;source counter set to translation table
          MOV BTABL,OFFSET UNP_NENV;indicates table to use
          MOV COUNT_V2,0           ;reset envelope byte counter
          CALL UPDAT               ;read data

          ADD COUNT_V1,128    ;increment voice byte counter
          POP CX              ;restore main voice counter
          LOOP VCS            ;read another voice
          RET                 ;return to main program

; This section reads voice data,unpacks it as needed and sends
; data to the output buffer

UPDAT:    MOV AL,[SI]    ;source data location in HSBUF is read
          PUSH SI        ;store source pointer
          CMP AL,99      ;see if source has to be unpacked
          JNE TR1        ;if not proceed
          MOV AX,0       ;reset register
          MOV SI,BTABL   ;reset pointer to bit map table
          CALL SC_LOP    ;unpacks source data
          JMP UNP2

TR1:      MOV BX,DXINPUTFILE_OFFSET  	;get starting offset in buffer
	    MOV SI,OFFSET HSBUF 		;source pointer is reset to start of
                              		;input buffer
	    ADD SI,BX		;set starting offset
          ADD SI,COUNT_V1     ;increment for no voices read (128)
          ADD SI,COUNT_V2     ;increment for envelopes read (17)
          CBW                 ;convert source data location to word format
          ADD SI,AX           ;gives offset required
          MOV AL,[SI]         ;transfer source to register

UNP2:     MOV [DI],AL         ;put into output buffer
          POP SI              ;restore source data pointer
          INC DI              ;increment output buffer pointer
          INC SI              ;increment input buffer pointer
          LOOP UPDAT          ;recycle for another byte of envelope data

;         Routine unpacks bits and places adjusted data
;         in output buffer.Two translation tables are used: UNP_ENV for
;         envelope data, and UNP_NENV for other voice data.Both have the same
;         general format. There are 4 bytes for each source:

;  counter value (ie CX),low bit no. of target,high bit no.,source offset

;    we begin by scanning translation table to find CX identifier


SC_LOP:   MOV AL,[SI]    ;read a counter value from table
          CBW            ;needs to be word format for comparison
          CMP CX,AX      ;is this the correct pointer
          JE UPCK        ;if it is proceed
          ADD SI,4       ;otherwise increment counter to next section
          JMP SC_LOP     ;and countinue scan
          JMP ERT        ;must be an error so terminate

UPCK:     MOV DX,0       ;reset register
          MOV DH,[SI+2]  ;this is the upper bit number of the target
          MOV DL,[SI+1]  ;this is the lower bit number of the target
          MOV AL,[SI+3]  ;this is the offset of the source byte

; To unpack the source byte we need to rotate the bits until the upper
;bit is in bit 7 position.Then  we need to shift the bits so that the lower
; bit is in bit 0. This will ensure that all bits above the upper target
; bit are zero. To get the correct rotation add 1 to the upper bit no.

          ADD DH,1
          PUSH CX        ;save counter
          MOV CL,DH      ;this is the rotation counter

;now we get the source byte

          MOV BX,DXINPUTFILE_OFFSET  	;get starting offset in buffer  
	    MOV SI,OFFSET HSBUF ;starting address of input buffer
          ADD SI,BX		;set starting offset
	    ADD SI,COUNT_V1     ;increment for number of voices read (128)
          ADD SI,COUNT_V2     ;increment for number of envelopes read
                              ;will be zero for non envelope data
          CBW                 ;convert address to word format
          ADD SI,AX           ;this gives the absolute address of the source

; now we can rotate the source

          MOV AL,[SI]         ;put source byte in register
          ROR AL,CL           ;rotation

;now we can shift the rotated byte so that the low bit is in bit0 position
; This is done by determining the number of bits and subtracting from 8
; the result is used as the shift counter

          SUB DH,DL           ;subtract low from high bit number
          MOV CH,8            ;put bit total in register
          SUB CH,DH           ;this gives the shift counter

          MOV CL,CH           ;CL is required counter register
          SHR AL,CL           ;target is now set with low bit at bit 0 posn
          POP CX              ;restore counter

          RET                 ;all done so return


;Routine to pack bytes in 32 voice format and place in I/O buffer


PACK:     MOV CX,32                ;number of voices for main loop
          MOV COUNT_V1,0           ;reset counters
          MOV COUNT_V2,0           ;reset counters


K1:       PUSH CX                  ;store voice counter
          MOV CX,6                 ;envelope counter

K2:       PUSH CX                  ;store envelope counter
          MOV CX,21                ;counter for envelope data
          MOV DI,OFFSET ENV_DATA   ;source pointer for I/O translator
          MOV BTABL,OFFSET UNP_ENV ;indicates table to use
          CALL OUTDAT              ;read data
          ADD COUNT_V2,17          ;increment envelope counter
          POP CX                   ;restore number of envelopes
          LOOP K2                  ;read another envelope

K3:       MOV CX,29                ;counter for rest of voice data
          MOV DI,OFFSET NON_ENV    ;translation table for non voice data
          MOV BTABL,OFFSET UNP_NENV;indicates table to use
          MOV COUNT_V2,0           ;reset byte counter
          CALL OUTDAT              ;read data

          ADD COUNT_V1,128         ;increment voice byte counter
          POP CX                   ;restore main voice counter
          LOOP K1                  ;read another voice

          RET                      ;end of routine


;Routine reads voice data in main voice buffer,packs it if required,and
;places it in I/O buffer

OUTDAT:   MOV AL,[DI]              ;output data location key is read
          PUSH DI                  ;store location key
          CMP AL,99                ;see if output data has to be packed
          JNE K4                   ;if not proceed

          MOV AX,0                 ;reset register
          MOV DI,BTABL             ;reset pointer to bit map table
          CALL BITPK               ;pack output data
          JMP K5

K4:       MOV DI,OFFSET HSBUF      ;pointer is reset to start of output buffer
          ADD DI,COUNT_V1          ;increment for no. of voices read
          ADD DI,COUNT_V2          ;increment for envelopes read
          CBW                      ;convert output data location to word format
          ADD DI,AX                ;gives offset required
          MOV AL,[SI]              ;transfer source to register
          MOV [DI],AL              ;put into output buffer

K5:       POP DI                   ;restore source data pointer
          INC DI                   ;increment I/O buffer pointer
          INC SI                   ;increment main voice buffer pointer
          LOOP OUTDAT              ;recycle for another byte of voice data
          RET


;Routine to pack I/O buffer data drawn from main voice buffer

;begin by scanning table to find source identifier

BITPK:    MOV AL,[DI]              ;read counter value from table
          CBW                      ;needs to be word format
          CMP CX,AX                ;see if corresponds
          JE PCK                   ;if yes then proceed
          ADD DI,4                 ;otherwise increment counter to next section
          JMP BITPK                ;continue scan until target found

PCK:      MOV DX,0                 ;reset register
          MOV DH,[DI+2]            ;this is the upper bit number to pack to
          MOV DL,[DI+1]            ;this is the lower bit number to pack to
          MOV AL,[DI+3]            ;this is the offset of the I/O buffer byte

          MOV BL,[SI]              ;get source byte
          PUSH CX                  ;store counter
          MOV CL,DL                ;this is the shift counter
          SHL BL,CL                ;adjust source byte

          MOV DI,OFFSET HSBUF      ;starting address of I/O buffer
          ADD DI,COUNT_V1          ;increment for voices read
          ADD DI,COUNT_V2          ;increment for envelopes read
          CBW                      ;convert offset
          ADD DI,AX                ;DI now has offset of current target byte
          ADD [DI],BL              ;add the 'packed' data

          POP CX                   ;restore counter
          RET



;routine to print to screen bank number being downloaded

BNKPRT:  MOV AX,CX
          MOV DX,0
          DIV BNDIV ;divide by 128 to get bank no
          MOV AH,0
          DIV TEN   ;convert to decimal AL contains tens,AH units
          PUSH AX
          CMP AL,0  ;see if its less than 10
          JG DXS5
          MOV AH,01e
          MOV AL,0
          ES MOV [DI+12],AX

DXS5:     ADD AL,48
          MOV AH,01e
          ES MOV [DI+12],AX

DXS6:     POP AX

          ADD AH,48
          MOV AL,AH
          MOV AH,01e
          ES MOV [DI+14],AX

          RET



;T33.txt


;Routines to read data from main I/O buffer to screen and vice versa

;	VCERED,DIG,DUMP,ZECHK,ASC_PRNT

;routine to read in a voice from main buffer for display

VCERED:    CMP NOVCS,32                  ;see if we have a bank loaded

           JE RA1                        ;if not read in from file
           CALL LIB
           
		CMP NOVCS,1                   ;see if a single voice was loaded
							;or esc pressed (novcs=0)
           JBE VCE1                        ;if not get selection from bank
           CALL DUMP                     ;reset screen before voice select
           JMP RA1
VCE1:
      MOV VNUM,0                    ;otherwise read voice to in-buf
           JMP R1

RA1:      CALL BV_SEL              ;select voice or new read in from file
          PUSH AX                  ;store command input
          CALL DUMP                ;reset screen
          POP AX

          CMP AL,76                ;see if L pressed
          JE RA2                   ;if it was read new data in from library
          CMP AL,108
	  JE RA2

          CMP AL,27                ;see if ESC pressed
          JNE R1
          RET

RA2:      CALL LIB
          CALL DUMP
          CMP NOVCS,1              ;see if only a single voice was read
          JNE VCERED
          MOV VNUM,0

R1:       MOV SI,OFFSET BUF
R1A:      MOV CX,155
          MOV DI,OFFSET IN_BUF
          MOV AX,155
          MUL VNUM                 ;multiply by number of voices
          ADD SI,AX

RA5:      MOV AL,B[SI]
          MOV [DI],AL
          INC SI
          INC DI
          LOOP RA5



;read byte from input buffer and use translation table to identify
;offset in output buffer

RA6:      MOV CX,145
          MOV SI,OFFSET IN_BUF
          MOV DI,OFFSET VTABL          ;start of translation table


MLOOP:    PUSH CX                       ;store counter
          MOV DX,OFFSET OUT_BUF

          MOV AL,[DI]                   ;put row in AX
          DEC AL
          MOV BL,160
          MUL BL
          PUSH AX

          MOV AL,[DI+1]                 ;put column in AX
          DEC AL
          CBW
          SHL AX,1
          POP BX
          ADD BX,AX                   ;BX now contains offset for output
          ADD BX,DX                   ;this gives absolute address for screen

;now we need to get the ASCII 2 digit value and put this in the screen
;address,ie BX and BX+2

          MOV AL,[SI]                 ;store number to be displayed
          MOV VDST,AL
          CALL ASC_PRNT                  ;put the number in the output buffer

ELOOP:    POP CX                        ;restore counter
          INC SI
          INC DI,2

          LOOP MLOOP                    ;read another byte

;next section puts voice name in buffer

          MOV DI,OFFSET OUT_BUF
          ADD DI,198                     ;DI is now at starting offset
          MOV CX,10

T3:       MOV AL,[SI]                 ;read voice name to buffer
          MOV [DI],AL
          INC SI
          INC DI,2
          LOOP T3

          CALL DUMP           ;dump voice screen


R6:       RET                 ;return to main command loop


;Routine DIG to update main voice buffer from screen


DIG:      MOV DI,OFFSET BUF   ;read to main voice buffer
DIG1:     MOV CX,145
          MOV SI,OFFSET VTABL ;table to translate screen positions
          MOV AX,0            ;reset register
          PUSH DI             ;default start of current voice
          CMP NOVCS,2         ;see if there's only one voice
          JB G2

          MOV AL,155          ;otherwise multiply by number of voices
          MUL VNUM
          ADD DI,AX           ;DI is now at start of current voice
          POP AX              ;reset
          PUSH DI             ;store for later use

G2:       MOV AX,0            ;reset register
          MOV AL,[SI]         ;move row number to register
          DEC AL              ;adjust to end of previous row
          MUL ROW
          PUSH AX


          MOV AX,0
          MOV AL,B[SI+1]
          DEC AL
          CBW
          SHL AX,1
          POP BX
          ADD BX,AX           ;BX has absolute source offset
          PUSH BX             ;store the location

G5:       ES MOV DL,[BX]      ;get the 'tens' part
          CALL ZECHK          ;reset rubbish

G6:       MOV AH,0
          MOV AL,DL
          SUB AL,48           ;convert from ASCII
          MUL TEN             ;multiply by ten
          MOV AH,0

          POP BX              ;restore location
          ES MOV DL,[BX+2]    ;get the 'units' part
          CALL ZECHK

G7:       MOV AH,0
          SUB DL,48           ;convert from ASCIIa
          ADD AL,DL           ;this gives the actual number

          MOV [DI],AL         ;update main buffer
          INC DI
          INC SI,2

          LOOP G2

G7A:      POP DI              ;restore starting offset for current voice
          MOV AX,198          ;starting offset of voice name on screen
          MOV SI,AX           ;source register
          MOV BX,145
          MOV CX,10

G3:       ES MOV AL,[SI]
          MOV [DI+BX],AL
          INC DI
          INC SI,2
          LOOP G3

G4:       RET                 ;buffer updated return to main program



;routine to dump video buffer to the screen


DUMP:     MOV SI,OFFSET OUT_BUF   ;assign SI to start of buffer
          MOV DI,0           ;DI now contains video memory offset


          CLD
          MOV BL,25           ;number of rows

A03:      MOV CX,160          ;load/reload bytes/row into CX
          REP MOVSB           ;move one row
          DEC BL              ;decrement row counter
          JNZ A03             ;if more rows move another one

          CSR 0,25                 ;position cursor
          RET

;routine checks screen output for non valid characters and resets to zero

ZECHK:         CMP DL,47
               JA  ZE1
               MOV DL,48
ZE1:           CMP DL,58
               JB ZE2
               MOV DL,48
ZE2:           RET

;routine to write ASCII values to output buffer

ASC_PRNT:      MOV DX,0            ;reset DX

               MOV AL,VDST         ;put target in register

               MOV AH,0            ;reset register
               DIV TEN             ;divide so that AL contains tens
                                   ;and AH units

               CMP AL,0            ;see if it's<10
               JNE ASC2
               MOV B[BX],32         ;reset character
               JMP ASC3

ASC2:          ADD AL,48           ;convert to ASCII value
               MOV [BX],AL         ;put in output buffer

ASC3:          ADD AH,48           ;convert to ASCII
               MOV [BX+2],AH       ;put in buffer
               RET

;!R! ; RES; FONT9; EXIT;

;T4.TXT  LIBRARY FILE HANDLER

LIB:      MOV AX,CPOS
          CMP CPOS,3558       ;I/O operations
          JE RET
          CMP CPOS,3578
          JE RET
          PUSH AX             ; store command cursor location

;routine to set up initial screen

L1:       MOV AX,98             ;set up initial screen
          CALL HELP
;now open file and if OK list first 3 banks to screen

	   CALL LIBOPEN
	   CMP AX,99		;terminate file operation -either an 
				;error on opening or new bank save
	   JE L2

;now call command processor 

	  CALL CMPROC	

;close library file and return to  main command processor

L2:       POP AX                   ;reinitialise cursor location
          MOV CPOS,AX
          MOV AH,62                ;close file
          MOV BX,HANDLE
          INT 21H
          RET                      ;end of library routine




;t41.txt

;Routines supporting Library :file handler LIBOPEN and display 3 banks 
; BANK_DISP

;Routine to open library file and list contents of first 3 banks to screen

LIBOPEN:  	MOV AH,61           		;function to open file
          	MOV AL,2            		;read/write mode
          	MOV DX,OFFSET LIBFIL		;file address
          	INT 21H             		;to DOS
          	JC OP1              		;error on opening
          	MOV HANDLE,AX       		;store file handle
          	JMP OP7

; create a new library file

OP1:      	MOV AH,5BH
          	MOV CX,0
          	INT 21H

          	JC OP4              		;error -file may already exist

OP2:      	MOV HANDLE,AX       		;success,save handle
          	CMP CPOS,3618       		;only proceed for write operation

          	JNE OP3             		;close file & return

	    	MOV AX,99	       		;flag to indicate new libary being created
  	    	CALL savevb	       		;save voice/bank in new file

OP3:	  	MOV AX,99				;terminate file operation on return
	  	MOV NOVCS,1
         	RET

;determine error and if necessary delete existing file


OP4:      CMP AX,80           ;see if file does already exist
          JNE OP5             ;wrong path,no handle available,or access denied 

          MESSAGE 3522,117,FEXIST,61,25,0         ;send warning message
          MOV AH,01
          INT 21H                                 ;get input

          CMP AL,89                               ;this means YES,new file
                                                  ;is required

          JE OP6


OP5:      MESSAGE 3522,117,FNAVAL,28,25,0         ;file can't be opened
          JMP OP3

OP6:	  LBLANK
          MOV AH,60           ;create new library file

          MOV DX,OFFSET LIBFIL
          MOV CX,0
          INT 21H

          JC OP3              ;failure !-abort operation
          JMP OP2             ;succes-proceed only with write

;now determine length of file

OP7:      MOV AH,66           ;function to move file pointer
          MOV AL,2            ;byte offset from EOF
          MOV CX,0            ;upper part of offset
          MOV DX,0            ;lower part of offset
          MOV BX,HANDLE       ;file handle
          INT 21H             ;to DOS

          MOV SI,OFFSET FILSIZ
          MOV [SI],DX         ;the file length MSB
          MOV [SI+2],AX       ;the file length LSB

          MOV BX,4960
          DIV BX              ;determines number of banks
          MOV BANKS,AX

;now display first page of file names

OP8:        MOV AX,PAGC
	  	MUL THREE

op10:	  	CALL BANK

op50:       CALL BANK_DISP      	;call routine to display
	  	MOV AX,0			;return code
	  	RET



;routine to display titles of 3 banks of data on screen

BANK_DISP:MOV DI,806               ;set up blank screen
          MOV CX,3                 ;counter for bank/voices
          MOV AL,32

BS0:      PUSH CX
          MOV CX,32                ;voice counter

BS1:      PUSH CX
          MOV CX,10
          MOV AH,31                ;attribute
          REP STOSW                ;write blank char to screen
          ADD DI,140               ;move to next line
          POP CX
          CMP CX,17                ;see if we are at bottom of column
          JNE BS2
          SUB DI,2534              ;to move to next column

BS2:      LOOP BS1                 ;blank another title
          POP CX
          SUB DI,2534
          LOOP BS0

		mov dx,0		
          MOV AX,BANKNO            ;store low bank number
	    DIV THREE
	    MOV PAGC,AX
	    MUL THREE
          MOV BX,4960              ;number of bytes per bank
          MUL BX                   ;AX contains lower half of byte count
                                   ;DX the upper half

;set file pointer to first name of first bank

          MOV CX,DX                ;the upper half of offset
          MOV DX,AX                ;the lower half
          MOV AH,66                ;file pointer function
          MOV BX,HANDLE            ;the file handle
          MOV AL,0                 ;from beginning of file
          INT 21H                  ;to DOS


          MOV SI,OFFSET FILSIZ     ;see if we are at end of file
          CMP [SI],DX              ;check MSB first
          JAE BA2
          CMP [SI+2],AX            ;check LSB
          JAE BA2

BA1:      BEEP
          JMP BA8                   ;if we are beep and return

;initialise buffer

BA2:	  MOV CX,3
	  PUSH CX

          MOV DI,OFFSET HSBUF
          MOV CX,5000
          MOV AL,32

BA2A:     MOV [DI],AL
          INC DI
          LOOP BA2A

;now read titles for 3 banks

          MOV DI,806               ;initial screen location
	  POP CX

BA2B:	  PUSH CX

          MOV AH,63                ;read function
          MOV BX,HANDLE
          MOV CX,4960              ;32 voice read from current posn
          MOV DX,OFFSET HSBUF    ;current voice buffer
          INT 21H


BA3:      MOV CX,32                ;voice counter
          MOV SI,OFFSET HSBUF
          ADD SI,145               ;get to first voice name


BA5:      PUSH CX                  ;store voice indicator
          MOV CX,10                ;ten bytes per voice name
          MOV AH,31                ;attribute

BA6:      LODSB                    ;load a character
          STOSW                    ;write it with its attribute to screen
          LOOP BA6                 ;display another character

          ADD DI,140               ;move to next line
          ADD SI,145               ;mov to next name
          POP CX                   ;restore voice counter
          CMP CX,17                ;see if we are at bottom of column
          JNE BA7
          SUB DI,2534              ;to move to next column,subtract 16 lines
                                   ;less 26 bytes
BA7:      LOOP BA5                 ;display another voice

          SUB DI,2534              ;move to next column

;now check for EOF

	  MOV AH,66
          MOV BX,HANDLE            ;the file handle
          MOV AL,1                 ;from current location
	  MOV CX,0
	  MOV DX,0
          INT 21H                  ;to DOS

	  POP CX		   ;restore bank counter
BA7B:     MOV SI,OFFSET FILSIZ     ;see if we are at end of file
          CMP DX,[SI]              ;check MSB first
          JB BA7C
          CMP AX,[SI+2]            ;check LSB
	  JAE RET
BA7C:     LOOP BA2B


BA8:	  RET


;T42.txt

; Command processor routine for READ,WRITE,FILE and RANDOMISE options


;         >>>> READ <<<<<<

CMPROC:   CMP CPOS,3598
          JNE L42
          CALL LIBEDIT
	    jc ret				;carry flag means Esc pressed so
						;just skip back to data entry

;set file pointer to beginning of current bank

L40:      MOV AX,BANKNO            ;identify bank to be loaded
          MOV DX,0                 ;reset MSB
          MOV BX,4960
          MUL BX

          MOV CX,DX                ;MSB offset
          MOV DX,AX                ;LSB offset for file pointer
          MOV AH,66                ;reset file pointer
          MOV AL,0                 ;from beginning of file
          MOV BX,HANDLE
          INT 21H

          CMP BIND,99              ;see if we are loading a bank
          JNE L41

          MOV CX,4960              ;number of bytes to read
          CALL BUFILL              ;read then in
          MOV NOVCS,32             ;set voice count
          JMP L42                  ;return

L41:      MOV AX,VOICENO           ;put voice no in register
          DEC AX                   ;adjust so first voice is 0
          MOV BX,155               ;number of bytes per voice
          MUL BX

          MOV DX,AX                ;now increment file pointer
          MOV CX,0
          MOV AH,66                ;file pointer function
          MOV AL,1                 ;from current location
          MOV BX,HANDLE
          INT 21H

          MOV CX,155               ;number of bytes to read
          CALL BUFILL              ;read them in
          MOV NOVCS,1              ;set voice count
;	  CMP BIND,98		   ;see if we are auditioning voice
	  RET		

;>>>>>  WRITE  <<<<<

L42:      CMP CPOS,3618       ;save a single voice or bank
          JNE L71
	    xor ax,ax		;flag, if set to 99 indicates new library being created
          CALL SAVEVB         ;subroutine to save single voice or 1/32 bank
          RET

;>>>>>  RANDOMISE  <<<<<

L71:      CMP CPOS,3758       ;select target voice for randomise
          JNE RET

L72:      CALL LIBEDIT

          CMP BIND,99         ;see if bank has been selected
          JNE L73             ;if so recycle
          BEEP
          JMP L72

L73:      MOV AX,BANKNO       ;set file pointer to start of bank to be loaded
          MOV DX,0
          MOV BX,4960
          MUL BX

          MOV CX,DX           ;MSB offset
          MOV DX,AX           ;LSB offset for file pointer
          PUSH DX             ;stack bank offset

          MOV AX,VOICENO      ;now add to start of selected voice
          DEC AX              ;adjust so first voice at 0
          MOV BX,155          ;number of bytes per voice
          MUL BX

          POP DX
          ADD DX,AX           ;DX now has offset of selected voice

;potential problem here if ax exceeds FFFF on addition


          MOV AH,66           ;set file pointer
          MOV AL,0            ;from start of file
          MOV BX,HANDLE
          INT 21H

;read voice data to randomise voice buffer

          MOV CX,155
          MOV DX,OFFSET RAND_BUF
          MOV AH,63           ;read from library file
          MOV BX,HANDLE
          INT 21H
	  RET




;*************************** savevb *********************************
;
;		Saves single voice or 1/32 voice bank
;		Determines whether we have anything in the library file
;		if not proceed to write new data otherwise ask if we are to save at 
;		current EOF or overwrite an existing voice or bank
;
;		input: AX, if set to 99 indicates new library file being created
;			     so no need to get user input on EOF/overwrite
;		output:
;
;**********************************************************************

SAVEVB:	  	cmp ax,99
			je sa250

			LBLANK
          		INPUT 3582,158,VSAL,45,25,0,79,1,1 ;save voice/bank at EOF or overwrite?
 
sa50:     		CMP AL,27           			;see if ESC pressed to abort
          		JNE sa200
     
sa100:     		CALL RESCOM	      			;restore command line
             
sa150:	  	MOV CPOS,AX
          		MOV AH,62                		;close file
          		MOV BX,HANDLE
          		INT 21H
          		RET                      		;end of library routine

sa200:     		CMP AL,13           			;write at EOF 
          		JNE sa400            			

sa250:		CMP NOVCS,32        			;if a bank is loaded need to get user
	  		jne sa300					;input on whether to save voice or bank

sa280:		call voiceor_bank
			jnc sa600					;user aborted if carry set		
          		jmp savevb

sa300:	  	MOV SI,OFFSET FILSIZ			;single voice save
	  		CMP W[SI+2],0	      		;see if its the first single voice in new library
	  		jne sa280

sa350:	  	MOV AX,99	      			;start of new bank
	  		MOV CX,155	      			;number of bytes to write
	  		call write_lib
			jmp sa600


sa400:      	CMP AL,"O"           			;indicates overwrite existing voice/bank
          		JE sa450             			
			cmp al,"o"
			jne sa500

sa450:		call overwrite_vb			
			jmp sa600

sa500:          	BEEP						;incorrect entry - recycle
          		JMP savevb

sa600:		ret


;************************** voiceor_bank ***********************************
;
;			Determines whether voice or bank is to be saved
;			If the former calls search lib to determine if any
;			blank space is available before EOF and if there is
;			saves the voice to that location. In both cases
;			write_lib is called to save data
;
;
;		Input:none
;		Output: carry flag is set if user decides to abort 
;			  otherwise carry is clear
;
;*****************************************************************************

voiceor_bank:     INPUT 3582,158,VSAV,45,25,0,66,1,1 	;save voice or bank at location
          		CMP AL,27           			;abort
          		JNE vob50
			stc
			ret

vob50:		CMP AL,13           			;default is single voice
			jne vob100
          		call search_lib				;see if any space available before EOF
			call write_lib 				;write voice at location or EOF
			ret
          		

;set up for bank write

vob100:		cmp al,"B"
			je vob150
			cmp al,"b"
			je vob150
			beep
			jmp voiceor_bank


vob150:          	MOV AH,66           			;set file pointer to EOF
          		MOV AL,02
          		MOV BX,HANDLE
          		MOV CX,0
          		MOV DX,0
          		INT 21H
          		MOV CX,4960         			;bank to write
          		MOV BIND,99         			;set bank indicator flag
          		MOV AX,0
          		
			call write_lib
			ret

;************************* overwrite_vb *********************************
;
;		Overwrites voice/bank at current cursor location
;
;		input:
;		output:
;
;********************************************************************

overwrite_vb:	LBLANK
	  		CMP NOVCS,32	      		;see if we are just overwriting from a 
									;single voice
	  		JE ow50
	  		MESSAGE 3530,30,VWRITE,71,25,0
	  		JMP ow100

ow50:     		MESSAGE 3530,30,VBWRITE,71,25,0
          
ow100:	  	CALL LIBEDIT        			;to select voice/bank
          		CALL VCE_ID         			;to identify location
          		CALL SAVSEL         			;to set file pointer and byte count
                              				;single voice or 1/32 bank write
          		MOV CSIZE,5         			;reset cursor size
          		MOV AL,0            			;reset new bank flag
          		call write_lib
			ret


;**********************  write_lib **********************
;
;			writes voice/bank data to file
;
;	input:	AX is set to 99 indicating new bank being started or 0, no new bank
;	output:	carry set if write fails
;
;**************************************************************

write_lib:      	PUSH AX					;store new bank indicator
			MOV BX,HANDLE
          		MOV DX,OFFSET BUF
          		CMP BIND,99                		;see if bank indicator flag is set
          		JE wl100

wl50:          	PUSH DX                    		;single voice write
          		MOV AL,155                 		;set offset for single voice
          		MOV AH,0
          		MUL VNUM
          		POP DX
          		ADD DX,AX

wl100:      	MOV AH,64           			;write 1/32 voices to file
          		INT 21H             			;save data
          		POP AX              			;retrieve new bank indicator
			jc ret

wl150:          	CMP AX,99           			;indicates start of new bank on single voice
          		JNE wl200
          		CALL NEWBNK         			;set next 31 voices

wl200:      	clc
			RET

;****************************  search_lib ***************************************
;
;		Searches library to see if all voices to current EOF are allocated
;
;		Input: none
;		Output:AX set to 0 to indicate no new bank require or 99 if EOF write
;
;*******************************************************************************

search_lib:      	MOV AH,66           			;set file pointer first
          		MOV AL,0            			;to beginning of file
          		MOV BX,HANDLE
          		MOV CX,0
          		MOV DX,0
          		INT 21H             			;file pointer now at beginning of file
          		MOV CX,BANKS
			cmp cx,0					;nothing in file so write at beginning
			je sl400

;Read bank into buffer

sl50:     		PUSH CX             			;store bank counter

          		MOV AH,63           			;read a bank to buffer
          		MOV DX,OFFSET HSBUF
          		MOV CX,4960
          		INT 21H
	  		MOV DI,0

;now search bank to see if all voices are allocated

          		MOV AL,0            			;zero register
          		MOV CX,32
          		MOV SI,OFFSET HSBUF

sl100:      	PUSH CX             			;store voice counter
	  		ADD SI,145
	  		MOV CX,10
	  		MOV DX,0					;use as flag set if voice name blank

sl150:     		MOV AL,[SI]

          		CMP AL,0      				;check whether voice name is blank
          		JE sl200
	  		CMP AL,32
	  		JE sl200
	  		MOV DL,1					;indicates voice data exists
	
sl200:	  	INC SI
	  		LOOP sl150
	  		CMP DX,1
	  		JE sl350

;current voice is blank so set file pointer

          		MOV AH,66           			;reset file pointer
          		MOV AL,0            ;
          		MOV BX,HANDLE
          		MOV CX,0     	    			;MSB current byte count
          		MOV DX,0          			;LSB current byte count
          		INT 21H

          		POP CX              			;tidy up stack
          		POP CX	      			;current bank count

sl250:	  	MOV AX,BANKS
	  		SUB AX,CX
	  		MOV CX,4960
	  		MUL CX
	  		MOV CX,DX
	  		MOV DX,AX
	  		MOV AH,66
	  		MOV AL,0
	  		INT 21H					;move file pointer to start of current bank

sl300:	  	MOV AH,66
	  		MOV AL,1
	  		MOV DX,DI
	  		MOV CX,0
	  		INT 21H		  			;file pointer now at start of blank voice

          		MOV CX,155          			;byte count for save
          		MOV AX,0            			;reset new bank flag
          		ret

sl350:      	ADD DI,155          			;increment byte count
          		POP CX              			;test another voice
          		LOOP sl100            			;loop until end of bank
          		POP CX
          		LOOP sl50          			;test another bank

          		;End of file so proceed to write

sl400:          	MOV CX,155
          		MOV AH,0
          		MOV AL,99           			;flag to indicate starting new bank
          		Ret




;Routine to blank out new bank following single voice read

NEWBNK:   		MOV CX,31           			;31 voices to blank
          		MOV BX,HANDLE


NW1:      		PUSH CX             			;store voice counter
          		MOV AH,64           			;write to file
          		MOV AL,0
          		MOV CX,155
          		MOV DX,OFFSET BLVC  			;buffer containing blank voice data
          		INT 21H
          		POP CX
          		LOOP NW1

          		ADD BANKS,1         ;increment total number of banks
          		RET



;Routine to determine whether to save voice or bank in 32 voice buffer


SAVSEL:   MOV AX,BANKNO       ;use bank number to determine posn in file
          MOV BX,4960
          MUL BX              ;DXAX now has bytes to beginning of current bank
          PUSH AX             ;store for later use
	  PUSH DX
          MOV SI,0            ;set byte count for buffer
          CMP BIND,99         ;see if only one voice in buffer
          JNE SEL1
          MOV BX,4960         ;byte count for bank save
          JNE SEL1
          POP CX              ;bank write
	  POP DX
          JMP SEL2

SEL1:     MOV BX,155          ;byte count for voice save
          MOV AX,VOICENO
          DEC AX
          MUL BX              ;bytes to start of current voice
          POP CX              ;restore bytes to bank
	  POP DX	      ;NOTE:problem if DX greater than FFFF on addition
          ADD DX,AX           ;bytes to selected voice

SEL2:     MOV AH,66           ;set file pointer
          MOV AL,0            ;from beginning of file
	  PUSH BX	      ;stack byte count for save
          MOV BX,HANDLE	      
          INT 21H             ;set file pointer
          POP CX              ;restore required byte count for write
          RET



;routine to read CX bytes from library file

BUFILL:   MOV AH,63                ;function to read from file
          MOV BX,HANDLE
          MOV DX,OFFSET BUF        ;main voice buffer
          INT 21H
          RET

;T43.txt

;Routines to allow cursor editing of screen data


;LIBEDIT,ENDLIB,,VCE_ID,PAGCH,BANK


;**********************  *******************************
;
;		Main control loop when in Library mode
;		initialises screen & cursor, gets user input
;		
;		input:none
;		output:if carry flag is clear a voice or bank will be loaded
;			 if the flag is set loading will be skipped and control
;			 returns to data entry screen
;
;******************************************************************

libedit:  	call lib_init				;initialise navigation data

libed100:   call lib_cset				;initialise cursor

libed200:   INPUT 3682,30,DY,1,25,0,66,1,0	;get user input - keycode in AX
								
          	CMP AL,27                      	;see if Esc or F10 was entered
          	JE libed210
		CMP ah,68
		jne libed220

libed210:	stc						;recycle to data entry screen without
		mov quit,2		
		ret						;loading voice/bank


libed220:	cmp al,0
		je libed300
		call lib_unextend				;actions that require exit from library
		jnc ret					;this gets CR and B - load voice or bank
		cmp al,-1					;incorrect entry - recycle
		jne libed100				;for Import/Export file set cursor & recycle
		jmp libed200				
		

libed300:	call lib_extend				;actions that remain within library
		jc libed100					;some actions require cursor to be reset
	    	jmp libed200				;recycle


;********************** lib_init *******************************
;
;		Initialises various data used for navigating
;		Library screen
;		
;		input:none
;		output:none
;
;******************************************************************


lib_init:	MOV RCOL,112                  		;reset column limits
          	MOV LCOL,8
          	MOV CMOV,26
          	MOV TROW,960
          	MOV BROW,3200
          	MOV CSIZE,10
          	MOV BIND,0                    		;reset 1/32 voice flag
		ret


;********************** lib_cset *******************************
;
;		Initialises library cursor
;		
;		
;		input:none
;		output:none
;
;******************************************************************




lib_cset: MOV CX,CSIZE                  		;cursor size
          MOV SI,LPOS
          CURSOR CSIZE,SI,124,COMCUR_BUF     	;set cursor and store current contents
          MOV CX,CSIZE
          MOV DI,LPOS
          ADD DI,CX                     		;set to end of cursor
          ADD DI,CX
          DEC DI,2
	    ret

;********************** lib_unextend *******************************
;
;		Processes unextended keypresses - CR,Esc and B -
;		in Library mode
;		
;		input:none
;
;		output:carry flag set if recycling or incorrect entry
;			 carry flag clear if voice/bank selected
;			 AL set to -1 if incorrect entry detected
;
;******************************************************************

lib_unextend:pusha 

		CMP AL,13                     ;see if CR was pressed
          	JNE lun300
          	CALL VCE_ID
          	clc
		jmp lun450
		
lun300:     CMP AL,66                     ;B=bank select pressed
          	JNE lun350
          	MOV BIND,99                   ;set bank flag
          	CALL VCE_ID                   ;set bank no. identifier
		clc
		jmp lun450

lun350:     cmp al,"I"				;import file
	      je lun370
		cmp al,"i"
		jne lun400
	
lun370:	call import_file
		cmp al,0				;see if all's OK
		jne lun380				;import failed
		call libopen			;read in first three banks of new library
		
lun380:	stc					;set cursor & recycle
		jmp lun450

lun400:	 cmp al,"E"				;export file
	      je lun410
		cmp al,"e"
		jne lun450
	
lun410:	call export_file
		jmp lun430				;recycle without setting cursor

lun420:	beep					;incorrect entry

lun430:	popa					;clean up stack
		mov al,-1				;set error return code
		stc
		ret

lun450:	popa
		ret



;********************** lib_extend *******************************
;
;		Processes extended keypresses - nav keys, ins/del etc
;		in Library mode
;		
;		input:none
;		output:carry flag set if cursor has to be reset in libedit
;
;******************************************************************



lib_extend:	  	Pusha

			CMP AH,82				;INS pressed to audition
	  		JNE libe100
	  		CALL AUDITION
			clc
	  		jmp libe800

libe100:	  	CMP AH,83				;DEL pressed to audition & play
	  		JNE libe150
	  		CALL AUDITION
			clc
	  		jmp libe800

libe150:	  	CMP AH,59				;F1 pressed for help
	  		JNE libe180
	  		MOV AX,3
	  		CALL HELP
			clc
 			jmp libe800

libe180:		CMP AH,68				;F10 - same effect as ESC
			jne libe200
			stc
			jmp libe800
	
libe200:	  	CMP AH,72                    	;cursor up routine
          		JNE libe250
			call lib_curup
			jmp libe800

libe250:      	CMP AH,80                    ;cursor down routine
          		JNE libe300
			call lib_curdn	
			jmp libe800

libe300:      	CMP AH,77           		;move cursor right
          		JNE libe350
			call lib_curt
			jmp libe800

libe350:      	CMP AH,75                    ;move cursor left
          		JNE libe450
			call lib_cult
			jmp libe800

libe450:      	CMP AH,117          		;cntrl-end for EOF
          		JNE libe500
          		CALL  ENDLIB
			stc
          		jmp libe800

libe500:      	CMP AH,119            		;cntrl home pressed
          		JNE libe550
          		MOV BANKNO,0
          		MOV AX,0
          		CALL BANK
          		CALL BANK_DISP
          		MOV LPOS,806
          		stc
			jmp libe800

libe550:	  	CMP AH,71				;home pressed
	  		JNE libe600
	  		CURLS LPOS,COMCUR_BUF		;get rid of cursor
	  		MOV LPOS,806
	  		stc
			jmp libe800

libe600:	  	CMP AH,79				;end (page) pressed
	  		JNE libe650
			call lib_ep
			stc
			jmp libe800
		
libe650:	  	CMP AH,73				;PgUp pressed
	  		JNE libe700
	  		CMP PAGC,0				;see if we're on first page
	  		JE libe750
	  		MOV LPOS,676
	  		MOV DIR,1
	  		CALL PA1
			stc
			jmp libe800


libe700:	  	CMP AH,81				;PgDn pressed
	  		JNE libe750
			call lib_pdn
			stc
			jmp libe800
		

libe750:		BEEP                		;incorrect entry - recycle
			clc


          		
libe800:		popa
			ret


;********************** lib_curup *******************************
;
;		Processes cursor up key
;		in Library mode
;		
;		input:none
;		output:carry flag set if cursor has to be reset in libedit
;
;******************************************************************

	
lib_curup:      	MOV DIR,1                    	;direction indicator
          		MOV AX,LPOS                  	;current location
          		CMP AX,TROW                  	;see if its on the top line
          		JB lcup50                     ;if it is shift to previous column or page
			CURS LPOS,160                 ; otherwise move a line
			stc
			ret

lcup50:	  	CMP LPOS,806 			;see if its the first entry on page
	  		JE lcup100
			CURS LPOS,-2374	       	;add 15 lines less 26 bytes
			stc
			ret

lcup100:	  	CMP PAGC,0				;dont move if already on 1st page
			clc
	  		JE ret
	  		ADD LPOS,2400			;move to bottom line before changing page
	  		CALL PAGCH
			stc
	  		ret


;********************** lib_curdn *******************************
;
;		Processes cursor down key
;		in Library mode
;		
;		input:none
;		output:carry flag set if cursor has to be reset in libedit
;
;******************************************************************


 lib_curdn:        MOV DIR,2                   	;direction indicator
          		MOV AX,LPOS                  	;current location
          		CMP AX,BROW                  	;see if on the bottom line
          		JAE lcdn50              	;if it is shift to next column or page
			CURS LPOS,160                 ;otherwise move down a line
			stc
			ret

lcdn50:	  	CMP LPOS,3336		    	;see if its the last entry on page
	  		JNE lcdn150

lcdn100:	  	SUB LPOS,2400			;move to top line before changing page
	  		CALL PAGCH
			stc
	  		ret

lcdn150:	  	CALL VCE_ID
          		MOV AX,BANKS        		;total number of voice banks
          		DEC AX
          		CMP BANKNO,AX       		;compares current bank no with total
          		Jae lcdn200

lcdn180:		CURS LPOS,-2374			;add 15 lines less 26 bytes
			stc
			ret

lcdn200:          MOV AX,VOICENO      	  	;check we are in second column,voice 17-32
          		CMP AX,16
          		JBE lcdn180               	;if we are at end recycle

          		BEEP
			clc
          		ret

;********************** lib_curt *******************************
;
;		Processes cursor right key
;		in Library mode
;		
;		input:none
;		output:carry flag set if cursor has to be reset in libedit
;
;******************************************************************


lib_curt:         CALL VCE_ID

          		MOV AX,BANKS        	;total number of voice banks
          		DEC AX
          		CMP BANKNO,AX       	;compares current bank no with total
          		JB lcrt100
          		MOV AX,VOICENO      	;check we are in second column,voice 17-32
          		CMP AX,16
          		JBE lcrt100      		;if we are at end recycle

lcrt50:     	BEEP
			clc
          		ret



lcrt100:     	MOV DIR,2           ;otherwise add to current offset
          		MOV AX,LPOS         ;current location
          		MOV DX,0
          		DIV LINE                     ;see where we are on current line

lcrt150:          MOV BX,RCOL
          		CMP DX,RCOL                  ;see if we are end of current page
          		Jae Lcrt200
		      CURS LPOS,CMOV                 ;otherwise move right
			stc
			ret
                        
lcrt200:          CALL PAGCH			;if so read in next page
			stc
			ret

;********************** lib_cult *******************************
;
;		Processes cursor left key
;		in Library mode
;		
;		input:none
;		output:carry flag set if cursor has to be reset in libedit
;
;******************************************************************



lib_cult:          MOV DIR,1                    ;subtract from current offset
          		MOV AX,LPOS                  ;current location
          		MOV DX,0
          		DIV LINE                     ;see where we are on curent line
        
lclt50:  		CMP DX,LCOL                  ;see if we are at left column limit
          		Jb lclt100                   ;if so change page
			CURS LPOS,CMOV               ;otherwise move left
			stc
			ret
          		
lclt100:		CMP PAGC,0                   ;see if we are on the first page
          		JE lclt150                   ;if so beep
          		CALL PAGCH                   ;otherwise change page
          		stc
			ret

lclt150:      	BEEP
			clc
	          	ret


;********************** lib_ep *******************************
;
;		Processes end (of current page) key
;		in Library mode
;		
;		input:none
;		output:carry flag set if cursor has to be reset in libedit
;
;******************************************************************


lib_ep:	  	CURLS LPOS,COMCUR_BUF		;get rid of cursor
	  		MOV LPOS,3336
	  		CMP BANKS,3				;see if on first page
	  		JB lep150				;CHECK THIS IS OK !!!!!

lep50:	  	MOV AX,BANKS
	  		SUB AX,BANKNO			;see if on last page
	  		CMP AX,2
	  		JG lep150

lep100:	  	CALL ENDLIB

lep150:	  	stc
			ret


;********************** lib_pdn *******************************
;
;		Processes page down key
;		in Library mode
;		
;		input:none
;		output:carry flag set if cursor has to be reset in libedit
;
;******************************************************************


lib_pdn:	  	CMP BANKS,3		;see if on first page & only 3 or less banks
	  		JB lpdn100

lpdn50:	  	MOV AX,BANKS
	  		SUB AX,BANKNO		;see if on last or penultimate page
	  		CMP AX,5
	  		JG lpdn150

lpdn100:	  	CALL ENDLIB
	  		stc
			ret

  
lpdn150:	  	MOV LPOS,3466
	  		MOV DIR,2
	  		CALL PA1
	  		stc
			ret
;********************* endlib *****************************
;
;
;		Shifts cursor to the last voice title
;
;		input:none
;		output:none
;
;************************************************************

ENDLIB:   	pusha
		CURLS LPOS,COMCUR_BUF         ;remove cursor
          
ENDL50:	MOV DX,0
		MOV AX,BANKS                  ;identify bankno of last page start
	    	DEC AX
          	DIV THREE
          	MOV PAGC,AX			    	;store page count	
          	MUL THREE
	    	INC AX

ENDL100:    MOV BANKNO,AX
	    	DEC AX


ENDL150:   	CALL BANK                      ;set titles
          	CALL BANK_DISP                 ;display bank


ENDL200:    MOV AX,BANKS
          	SUB AX,BANKNO
          	PUSH AX
          	MOV BX,52
          	MUL BX                        
          	MOV LPOS,806
          	ADD LPOS,AX           		;sets up cursor at first voice of last bank

ENDL250:	POP BX
          	MOV AX,4960
          	MUL BX                        
          	MOV SI,OFFSET HSBUF
          	ADD SI,AX                 	;puts si at start of current bank
          	MOV CX,2                      ;find last voice

ENDL300:    PUSH CX
          	MOV CX,16

ENDL350:    MOV AL,[SI]
          	CMP AL,255
          	JE ENDL500
          	ADD LPOS,160
          	ADD SI,155
          	LOOP ENDL350

ENDL400:    POP CX
          	SUB LPOS,2534
          	LOOP ENDL300

ENDL500:    SUB LPOS,160          		;adjust to last voice name location
          	CMP CX,0
          	JG ENDL600
          	ADD LPOS,2534


ENDL600:    popa
      	RET



;Routine to identify voice and bank location

VCE_ID:   MOV AX,LPOS                   ;determine where we are on line
          MOV DX,0
          DIV LINE
          MOV AX,DX
          MOV DX,0
          MOV BX,53                     ;number of columns per bank
          DIV BX
          MOV BANKNO,AX                 ;this gives bank no. containing voice
          MOV AX,PAGC                   ;to be added to page count
          MUL THREE
          ADD BANKNO,AX                 ;to give absolute bank number


          MOV AX,LPOS                   ;now determine voice no. in bank
          SUB AX,4                      ;adjust to start of voice no.
          MOV SI,AX                     ;on screen
          ES MOV AL,B[SI]               ;read in tens digit
          SUB AL,48                     ;convert from ASCII
          MOV AH,0
          MUL TEN                       ;multiply tens component
          PUSH AX
          ADD SI,2                      ;now get units
          ES MOV AL,B[SI]               ;read in units
          SUB AL,48                     ;convert from ASCII
          MOV AH,0
          POP BX
          ADD AX,BX
          MOV VOICENO,AX                ;store voice no.

          RET


;Routine to change page up or down

PAGCH:    MOV SI,LPOS
          CURLS LPOS,COMCUR_BUF           ;remove cursor at current location

PA1:      CMP DIR,2                      ;see which dir,1=left 2=right
          JE PA2
          ADD LPOS,130                   ;move cursor to final column
          SUB PAGC,1                     ;adjust page number
          MOV AX,PAGC
          MUL THREE
          CALL BANK                   ;reset bank identifiers
          CALL VCE_ID                 ;update bank/voice nos
          CALL BANK_DISP              ;display newset of 3 voices
          JMP PA4


PA2:      SUB LPOS,130                  ;move cursor to first column
          ADD PAGC,1                    ;adjust page number

pa2a:     MOV AX,PAGC
          MUL THREE
          CMP BANKS,AX                ;compare with total no of banks in file
          JAE PA3                     ;proceed if below or equal
          DEC PAGC                    ;otherwise reset page count
          ADD LPOS,130
          BEEP
          JMP PA4                     ;and recycle

PA3:      CALL BANK                   ;reset bank identifiers
          CALL VCE_ID                 ;update bank/voice nos
          CALL BANK_DISP              ;display newset of 3 voices

PA4:      RET



;routine to set/reset bank nos on shifting page

BANK:     	pusha

		MOV CX,3
          	MOV DI,296				;location of third bank identifier

bnk10:    	PUSH AX                  	;store page identifier 0,3,6 etc
          	ADD AX,CX                	;set bank no.
		mov dx,0
	    
bnk20:	DIV THOUSAND
		push dx				;store remainder
	
bnk30:	mov dx,0				;flag set to 0 - don't print remainder
		call bnkno_scr
		pop ax				;get thousands' remainder
		
		DIV HUNDRED				;ah now has reaminder (tens),AL hundreds
		mov dx,0				;flag set to 0 - don't print remainder
		
bnk40:	call bnkno_scr			;print hunder's value
		shr ax,8				;put ah, into al
		DIV TEN				;al now has tens, ah units
		
bnk50:	mov dx,1				;print remainder
		call bnkno_scr

bnk60: 	POP AX				;get page identifier
          	SUB DI,60
          	LOOP bnk10
		
bnk70:	popa
		ret

;************************* bnkno_scr ***************************
;
;
;		routine prints bank number to screen
;
;		input: AX - only AL used except for div by ten 
;				when ah has remainder (ie units)
;			 DX - flag, when set reaminder in AH is printed
;
;		output:none
;
;*****************************************************************************


          
bnkno_scr:  push ax
		push bx
		push dx

		ADD AL,48			;convert quotient to ascii
          	ADD AH,48			;convert remainder to ascii
          	MOV BH,30			;set attribute for remainder
          	MOV BL,AH			;put remainder in BX
          	MOV AH,30			;set attribute for quotient

bns10:      STOSW				;print quotient
	    	
bns20:	cmp dx,1			;see if we are printing remainder (only units)
		jne bns30
          	mov AX,bx			;get units
          	STOSW			      ;print units
         
bns30:	pop dx
		pop bx
		pop ax
          	RET

;    !R! ; res; font9; exit;

           ; T44.TXT - Miscellaneous routines to:

           ;      a) display voice bank titles for cursor selection (BV_SEL)
           ;      b) print 1/32 voices (PRINTV)
           ;      c) randomise voice parameters (RANDV)


;routine to save screen between lines 15 and 19 and read in titles

BV_SEL:   MOV AX,CPOS                   ;store soft cursor location
          PUSH AX
          CSR 0,25                      ;zap cursor
          SCRSAV HSBUF,14,7                   ;save screen
          
;blank top and bottom headers

          HEADBL 1152,3,48,112
          HEADBL 2912,3,48,112

SAV2:     MOV SI,OFFSET BUF
          ADD SI,145                   ;  set to start of first voice
          MOV CX,8                     ; 8 lines per bank
          MOV BX,1632                  ;start location
          MOV AH,112                     ;set attribute

SAV3:     PUSH CX                       ;store voice counter
          MOV CX,4                      ;4 voices per line

SAV4:     PUSH CX
          MOV CX,10                     ;10 bytes per voice

SAV5:     MOV AL,[SI]                   ;load data
          ES MOV [BX],AX                ;write to screen
          INC BX,2
          INC SI
          LOOP  SAV5

          POP CX
          MOV AH,112
          MOV AL,0
          ES MOV [BX],AX
          INC BX,2
          ES MOV [BX],AX
          INC BX,2
          ADD SI,145                    ;move to next title
          LOOP SAV4

          POP CX
          ADD BX,64                     ;move to next line
          LOOP SAV3

; Set cursor

BMS0:     MOV CSIZE,12        ;cursor size
          MOV CMOV,24         ;cursor movement
          MOV CPOS,1632       ;starting position line 8
          MOV SI,CPOS
          CURSOR CSIZE,SI,47,SCBUF     ;set cursor and store current contents
          MOV TROW,1758       ;upper row limit for cursor movement
          MOV BROW,2710       ;lower row limit for cursor movement
          MOV RCOL,100        ;right column limit for cursor movement
          MOV LCOL,52         ;left column limit for cursor movement



;get input on whether new voices are to read from file

BMES:     MESSAGE 1312,124,BSEMSG,48,25,0
          MESSAGE 3072,124,BLEMSG,48,25,0




; cursor routine


BMS1:     MOV AH,01           ;function to determine keyboard status
          INT 16H             ;to BIOS
          JZ BMS1               ;no input keep monitoring

          MOV AH,00           ;read waiting character
          INT 16H             ;to BIOS

          CMP AL,0            ;see if extended code was entered
          JE BMS2A

          CMP AL,76           ;LOAD new voices
          JE BMS13A
	  CMP AL,108
	  JE BMS13A


BSM13:    CMP AL,27           ;see if esc was pressed
          JNE BMS14            ;if it was restore screen contents from buffer

BMS13A:   PUSH AX             ;store terminating entry
          SCRES HSBUF,14,7          ;restore screen
          BLANK
          POP AX              ;restore terminating entry
          POP DX              ;resore soft cursor location
          MOV CPOS,DX
          RET                 ;end of routine

BMS2A:    JMP BMS2              ;staging post

BMS14:    CMP AL,13            ;CR to select voice
          JNE BMS15
          MOV AX,CPOS
          MOV DX,0
          DIV LINE             ;AX now has line no and DX remainder
          PUSH DX              ;stack remainder
          SUB AX,10            ;so that first voice starts at zero
          MOV CX,4
          MUL CX              ;now AX has voice no. of start of current row
          POP DX
          PUSH AX
          MOV AX,DX           ;put remainder in register
          SUB AX,32           ;first voice is in column sixteen
          MOV CX,20           ;determine voice in current row as 0-7
          MOV DX,0
          DIV CX              ;now ax has no on current line
          POP BX              ;restore voice no at start of current row
          ADD AX,BX           ;ax now has current voice number
          MOV VNUM,AX
          JMP BMS13A

BMS15:    BEEP                ;invalid entry
          JMP BMS1


BMS2:     CMP AH,72           ;cursor up routine
          JNE BMS5

BMS3:     MOV DIR,1           ;direction indicator
          MOV AX,CPOS         ;current location
          CMP AX,TROW          ;see if its on the top line
          JAE BMS4
          JMP BMS12

BMS4:     MOV BX,160            ;move cursor
          CALL B_CURM
          JMP BMS1               ;start input new data

BMS5:     CMP AH,80             ;cursor down routine
          JNE BMS9

          MOV DIR,2             ;direction indicator
          MOV AX,CPOS           ;current location
          CMP AX,BROW
          JA BMS12                ;see if its on last line
          JMP BMS4

BMS9:     CMP AH,77           ;move cursor right
          JNE BMS11

          MOV DIR,2           ;add to current offset
          MOV AX,CPOS         ;current location
          MOV DX,0
          DIV LINE            ;see where we are on current line
          MOV BX,RCOL
          CMP DX,RCOL          ;see if we are at limit of cursor mov.
          JAE BMS12              ;if not beep

BMS10:    MOV BX,CMOV
          MOV AX,CPOS
          CALL B_CURM
          JMP BMS1

BMS11:    CMP AH,75           ;move cursor left
          JNE BMS12

          MOV DIR,1           ;subtract from current offset
          MOV AX,CPOS         ;current location
          MOV DX,0
          DIV LINE           ;see where we are on curent line
          CMP DX,LCOL        ;see if we are at left column limit
          JNB BMS10            ;if not beep

BMS12:    BEEP
          JMP BMS1
          RET

;Routine to move cursor

B_CURM:   CURES             ;routine to move cursor
          MOV AX,CPOS
          MOV DL,DIR
          CMP DL,1            ;see if we are adding or subtracting
          JG BM1              ;if no is 2 we are adding
          SUB AX,BX           ;move up line or back column
          JMP BM2
BM1:      ADD AX,BX           ;mov down line or up column

BM2:      MOV CPOS,AX
          MOV SI,CPOS
          CURSOR CSIZE,SI,47,SCBUF
          RET


;Routine to print 1 or 32 voices


PRINTV:   MOV CPOS,AX         ;store command cursor location
          PUSH ES
          MOV AX,DS
          MOV ES,AX
          MOV SI,OFFSET BUF   ;set source buffer address offset
          MOV CX,1            ;set default voice nos for print
          CMP NOVCS,1            ;see if only single voice in buffer
          JE PV3

          INPUT 3840,30,PNTVC,38,25,0,66,1,1     ;single or 32 voice print

          MOV SI, OFFSET BUF

          CMP AL,13
          JNE PV0
          MOV AX,155
          MOV DX,0
          MUL VNUM
          ADD SI,AX                          ;set to start of current voice
          MOV CX,1
          JMP PV3

PV0:      CMP AL,27
          JNE PV2
          POP ES
          RET


PV2:      MOV CX,32           ;must be 32 voice print selected

PV3:      PUSH CX
          MOV CX,6          ;set for 6 operator data print

PV4:      PUSH CX
          MOV BX,6
          SUB BX,CX
          MOV AX,BX
          MOV BX,80
          MOV DX,0
          MUL BX              ;AX now has required offset in translate table
          MOV OPADJ,AX

          MOV BX,OFFSET PNTABL     ;set/reset to start of translation table
          MOV CX,21           ;21 data items per operator
          CALL PNTC           ;put operator data in print buffer
          POP CX              ;reset operator count
          LOOP PV4

          MOV CX,19           ;now put non operator data in buffer
          MOV OPADJ,0
          MOV BX,OFFSET PNTABL
          ADD BX,84           ;set to start of non operator offsets
          CALL PNTC

          MOV CX,10           ;now put voice name in buffer
          MOV DI,OFFSET PNTV_BUF
          ADD DI,54           ;start of voice name in print buffer

PV5:      MOV AL,[SI]       ;transfer data
          MOV [DI],AL
          INC SI
          INC DI
          LOOP PV5


          MOV BX,4            ;file handle for printer
          MOV CX,948          ;buffer length
          MOV DX,OFFSET PNTV_BUF   ;set to print buffer address
          MOV AH,64           ;write to file
          INT 21H

          POP CX              ;restore main voice counter
          LOOP PV3
          POP ES

          RET



PNTC:     MOV DI,OFFSET PNTV_BUF ;initialise print buffer dest.
          MOV AL,[SI]         ;get source
          MOV AH,0
          DIV TEN             ;AL contains tens,AH units
          PUSH AX
          MOV DX,[BX+2]
          ADD DX,OPADJ         ;adjust operator offset indicator for opno
          ADD DI,DX            ;set target byte for tens
          CMP AL,0             ;see if no is <10
          JE PC2

          ADD AL,48            ;convert to ASCII value
          MOV AH,0
	  CMP [BX],AH           ;check that there is a print space allocated
          JG PC1
          MESSAGE 3840,79,PNTERR,60,24,0  ;data error so terminate
          MOV AH,08
          INT 21H
          POP AX
          POP AX
          POP AX                ;tidy up stack
          POP CX
          POP ES
          RET

PC1:      MOV [DI],AL

PC2:      POP AX              ;now set units
          ADD AH,48
          INC DI
          MOV [DI],AH
          INC SI
          INC BX,4
          LOOP PNTC            ;get another byte of voice data
          RET



;Routine RANDV to randomise voices parameters

RANDV:    MOV RANENOP,0       ;zeroise selection flags
          MOV RANOPA,98

RN00:     CMP RANOPA,98        ;first time go straight to library
          JE RN0A

          MESSAGE 3840,30,RLIBSEL,68,25,0   ;select target voice
          MOV AH,01           ;get input
          INT 21H

          MOV AH,0
          CMP AL,27           ;see if ESC pressed
          JNE RN0
          BLANK
          RET

RN0:      CMP AL,13
          JNE RN0A
          CALL RN8
          JMP RN8A

RN0A:     CALL DIG            ;update main voice buffer from screen
          MOV DI,OFFSET RANRES_BUF ;store voice for possible reset
          CALL RANUP

          CALL LIB            ;select target voice and read to RAND_BUF
          MOV DI,OFFSET IN_BUF
          CALL RANUP
          JMP RN2

RANUP:    MOV SI,OFFSET BUF   ;update screen input buffer
          MOV CX,155
          MOV AX,155
          MUL VNUM            ;multiply by number of voices
          ADD SI,AX

RN1:      MOV AL,B[SI]
          MOV [DI],AL
          INC SI
          INC DI
          LOOP RN1             ;buffer updated
          RET

;decide whether all voice parameters are to be randomised


RN2:      MOV CSIZE,5         ;reset screen
          MOV QUIT,2
          CALL DUMP

          INPUT 3840,30,RANSEL,63,25,0,86,1,1    ;select envelope or all voice parameters

          CMP AL,86           ;this is for complete voice randomise
          JE RN3
        
          CMP AL,27           ;see if ESC pressed
          JE RET
          JMP RN3

RN8A:     JMP RN15

RN3:      MOV RANENOP,AL
          BLANK
          INPUT 3840,30,RANOP,58,25,0,65,1,0     ;single or all operators

          CMP AL,27           ;see if ESC pressed
          JNE RN4
          JMP RN2


RN4:      MOV RANOPA,99       ;set flag to indicate all 6 ops to be randomisd
          CMP AL,65           ;A pressed for all operators
          JE RN7A
          CMP AL,48           ;see if input between 1 & 6
          JA RN6

RN5:      BEEP
          JMP RN3

RN6:      CMP AL,54
          JA RN5

RN7:      SUB AL,48
          MOV AH,0
          MOV RANOPA,AX  ;set op flag - op 6 is first in buffer

RN7A:     CALL RN8

RN15:     MESSAGE 3840,30,RANCON,80,25,0    ;continue, or quit/reset
          MOV AH,01           ;get input
          INT 21H

          CMP AL,13
          JE RN15A

          CMP AL,27           ;see if ESC pressed
          JNE RN17
          CALL RESET          ; if so restore screen
RN15A:    JMP RN00            ;and recycle


RN17:     ;   start automated randomisation
	  BLANK
          MESSAGE 3840,30,RANAUTO,33,25,0   ;press ESC to stop
	  mov dx,1000
	  call pldel                     
	  MOV AX,0
          MOV AH,6
          MOV DL,0FFH
          INT 21H


RN18:     CMP AL,27
          JE RN15
          CALL RESET
          CALL RN8
          JMP RN17



;resets voice to pre randomisation parameters

RESET:    MOV SI,OFFSET RANRES_BUF
          MOV DI,OFFSET IN_BUF
          MOV CX,155

RN16:     MOV AL,B[SI]
          MOV B[DI],AL
          INC SI
          INC DI
          LOOP RN16

          CALL RA6
          RET


;main loop to set randomisation values

RN8:      MOV SI,OFFSET RAND_BUF
          MOV DI,OFFSET IN_BUF
          CALL SETRND         ;set starting point in random number matrix
          MOV CX,6

RN9:      CMP RANOPA,99           ;see if all ops flag set
          JE RN10
          MOV BX,RANOPA
          CMP CX,BX           ;see if selected operator
          JE RN10             ;if not skip to next operator
          ADD SI,21           ;increment byte counts
          ADD DI,21
          JMP RN14

RN10:     PUSH CX             ;stack operator counter
          MOV CX,21           ;bytes per operator

RN11:     CMP RANENOP,86           ;see if we are rand. all optr data
          JE RN12
          CMP CX,14
          JB RN13             ;skip non envelope data

RN12:     PUSH CX             ;store counter
          CALL GETRND         ;routine to randomise data
          POP CX

RN13:     INC SI
          INC DI
          LOOP RN11
          POP CX

RN14:     LOOP RN9

          MOV QUIT,2
          MOV CSIZE,5
          CALL RA6            ;initialise output buffer and dump screen
          CALL OUTP0            ;play voice
          CALL PLAYV
          RET



;Routine to set random number series seed

SETRND:   MOV AH,0            ;get current value of system clock
          INT 26

	  mov dh,0
	  add ranst,dx
	  cmp ranst,998
	  jbe sr1
	  sub ranst,998


SR1:      MOV dx,OFFSET RANDO
          ADD RANST,DX
          RET

;Routine to randomly set value in range between source and target values

GETRND:   MOV AL,B[SI]        ;from target
          MOV AH,B[DI]        ;from source
          PUSH SI
          MOV SI,RANST        ;set start

          MOV DX,AX           ;set register to upper limit
          CMP DH,DL           ;see if AH is higher
          JGE SR2
          MOV DH,AL
          MOV DL,AH

SR2:      MOV CL,1            ;digit counter
          MOV CH,0
          CMP DH,9            ;see if we are to read a 2 digit no.
          JA SR3
          MOV AL,B[SI]
          INC SI
          JMP SR4

SR3:      MOV CL,2            ;2 digits to be read
          MOV AL,B[SI]        ;read the ten's digit
          MUL TEN
          ADD AL,B[SI+1]
          INC SI,2


SR4:      CMP DH,AL           ;check that no. is in range
          JB SR50a
          CMP DL,AL
          JBE SR6             ;all OK no is in range

SR50a:      ADD SI,48
          CMP CL,1            ;see if its a 2 digit no.
          JE SR2
          DEC SI
          JMP SR2

SR6:      MOV B[DI],AL        ;put selected no. in target buffer
          MOV RANST,SI
          MOV AX,SI
          MOV SI,OFFSET RANDO
          SUB AX,SI

          CMP AX,999       ;see if we are at end of series
          JBE SR7
          SUB AX,999
          ADD SI,AX
          INC SI
          MOV RANST,SI    ;if so reset random number locator

SR7:      POP SI
          RET





;T45.txt Import/Export file routines


;*************************** import_file *********************
;
;	gets a dx file, checks it's format, converts
;	it if necessary to unpacked/no header format
;	and appends or replace the existing library file
;
;There are four types of file recognised unpacked/packed and with/withput header
;This routine checks the file type is one of the four recognised types by dividing the file
;length and check there is no remainder

;Unpacked - without header       155 bytes per voice , 4960 bytes per bank of 32 voices
;Unpacked - with header          163 bytes per voice , 5216 bytes per bank of 32 voices
;Packed   - without header       128 bytes per voice , 4096 bytes per bank of 32 voices
;Packed   - with header          128 bytes per voice , 4104 bytes per bank of 32 voices (128*32)+ 8
;
;
;	input: none
;	output: al,0 if all OK otherwise -1 for error/abort
;
;**************************************************************

import_file: 	

			scrsav CBRKBUF,24,1		;store the bottom line of library screen

if5:			call get_filename			;get the import file name
			cmp al,0
			jne if200j

if10:			call open_ifile			;open the import file
			jnc if20

if15:			BLANK
          		MESSAGE 3842,31,fileopenerr,64,24,66;ask user whether to recyle or abort
			jmp if40

if5j:			jmp if5
if200j:		jmp if200

if20:			call check_filetype		;check for valid DX file format
			cmp dxpacked,-1			;invalid file type
			jne if100

if30:			mov ah,62				;close the invalid file
			mov bx,ImpHandle			
			int 21h

if35:			BLANK
          		MESSAGE 3842,31,invalifile,61,24,63;ask user whether to recyle or abort
		
if40:			mov ah,12				;reset keyboard buffer then get the character	
			mov al,1
			int 21h

if50:			cmp al,27				;Esc pressed to abort
			jne if5j
			mov al,-1				;return code
			mov dxpacked,0			;reset packed banks indicator
			jmp if200
		
		
if100:		call apprep_dxlib			;append or replace DXLIB
			cmp al,-1				;abort or error
			je if200

		
if150:		call process_ifile		;process import file
			mov al,0				;return code - all OK
			jnc if200				;transfer to dxlib.dat

if180:		mov al,-1				;return error code, check BX for detail

if200:		push ax
			scres CBRKBUF,24,1		;restore the bottom line of the screen
			pop ax
			ret




;*********************** check_Filetype ***************************
;
;	Checks import file type by dividing length by permissible
;	bank lengths and finding one with zero remainder

;	input:none
;	output : the variable dxpacked is set as follows :
;
;		0 if file is unpacked without headers
;		1 if file is unpacked with headers
;		2 if file is packed without headers
;		3 if file is packed with headers
;
;	       -1 if file format is invalid
;
;
;*******************************************************************

check_filetype:

		mov cx,6
		mov si,offset impfiletype		;array containing permissible 
								;file/voicetype lengths

cft20:	push cx
		mov bx,w[si]				;get the next filetype index

cft40:	mov dx,impsize1				;store file size msb
		mov ax,impsize2				;the lsb
		div bx

cft60:	cmp dx,0					;see if remainder is 0
		je cft80

cft70:	pop cx
		add si,2
		loop cft20
		mov dxpacked,-1				;error not a valid filetype
		ret

cft80:	pop cx
		mov al,6
		sub al,cl
		mov dxpacked,al
		
		cmp dxpacked,4
		jne cft90
		mov dxpacked,2
	
cft90:	cmp dxpacked,5
		jne cft100
		mov dxpacked,0 	

cft100:	cmp dxpacked,6
		jne cft120
		mov dxpacked,1

cft120:	ret
	

;******************** open_ifile *************************
;
;
;	opens the import file and DXLIB.dat
;	stores length of input file for checking
;	and sets filepointer of dxlib.dat to end of file
;	so imported file can be appended
;
;	imput:none
;	output: carry flag clear if all OK otherwise carry flag set
;
;**********************************************************


open_ifile:

		pusha
	
		MOV AH,61				;open source file
		MOV AL,0				;read access
		MOV DX,OFFSET nam			;file name
		inc dx,2				;jump over first 2 bytes: buffer and file lengths
		INT 21H

 		JC open50				;file open error
		MOV ImpHandle,Ax			;store file handle

open10:	mov ah,66				;find size of file
		mov al,2
		mov bx,ImpHandle
		mov cx,0
		mov dx,0
		int 21H
		JC open50				;error finding size

		cmp dx,0				;check there's something in the file
		jg open20
		cmp ax,0
		je open50				;error - file empty

open20:		mov impsize1,dx				;store file size msb
		mov impsize2,ax				;the lsb

		mov ax,04200h				;reset file pointer to beginning of file
		mov cx,0
		mov dx,0
		mov bx,ImpHandle
		int 21h
		jc open50

open40:		clc					;all OK- clear carry
		popa
		ret

open50:		cmp ImpHandle,0
		je open60
		mov ah,62				;error occured during file open so close file
		mov bx,ImpHandle			;close import file
		int 21h
	

open60:		popa
		stc					;set carry for error in file opening
		ret



;************************ apprep_dxlib ***************
;
;	get's user input on whether to append
;	import file to library or replace it
;	if replacing, creates and opens new library file
;	if appending opens existing file and sets file
;	pointer to end of file
;
;	input:none
;	output: Al,0 if function completed OK, otherwise -1
;		if aborting (esc pressed,library file failed to open, or file pointer
;				couldn't be located to end of file)
;
;************************************************************

apprep_dxlib:	BLANK
          	MESSAGE 3842,31,ifilemsg,37,24,38;ask whether to replace dxlib or append
		
appr10:		mov ah,1			;get the character	
			int 21h
			cmp al,27			;Esc pressed to abort
			jne appr20

appr15:		mov al,-1			;return code
			ret

appr20:		cmp al,"R"			;see if we are replacing dxlib
			je appr30
			cmp al,"r"
			je appr30
			cmp al, "A"			;see if we are appending to exisiting DXLIB
			je appr60
			cmp al,"a"
			je appr60

			beep
			jmp apprep_dxlib		;recyle - invalid entry

			;create new DXLIB

appr30:		mov ah,62			;close DXLIB
			mov bx,Handle			
			int 21h
			jc appr15

appr40:		mov ah,60
			MOV al,2			;read/write access
			MOV DX,OFFSET Libfil	;file name
			INT 21H
			JC appr15			;provide for file open error
			MOV Handle,AX		;store file handle
			mov al,0			;all OK,return code
			ret

			;append to existing DXLIB

appr60:		mov ax,04202h		;set file pointer to end of file
			mov cx,0			;so new data can be appended
			mov dx,0
			mov bx,Handle
			int 21h
			jc appr15

appr80:		mov al,0			;all OK,return code
			ret


;********************* process_ifile **************************
;
;		processes import file converting it to DXLIB.DAT format
;		which is unpacked voices without header. Reads files
;		containing packed voices with/without headers and
;		files with unpacked voices with headers
;
;
;		input:none
;		output: BX=0 and carry clear if all read/transferred OK
;			otherwise carry set and BX carries error code
;
					;error codes in bx
					;-1 	EOF detected
					;-2 	nothing read
					;-3	unrecognised filetype
					;-4	error unpacking voices
;
;*********************************************************************

process_ifile:	mov ax,0			;initialise bank counter

							;banks loop here
pif100:	inc ax,1				;increment bank counter
		push ax				;stack bank counter 
		mov di,3980
		call scrpt_ascii			;display bank number

pif200:	call read_voices			;initialise bank buffer and read 32 dx voices to buffer
		push bx				;stack return code
							;0	full read

	
		mov dx,offset DXEXIM_BUF	;write bank to DXLIB.DAT
		mov cx,4960				;32 voice write 155*32 = 4960
		mov ax,04000h			;write to file function code
		mov bx,Handle			;library file handle
		int 21h

		pop bx				;get EoF flag
		pop ax				;get bank counter

pif300:	clc					;clear carry (error) flag
		cmp bx,0				;see if end of dx file has been reached or an error has occured
		jge pif500
	
pif400:	cmp bx,0FFFC			;check to see if error is between -1 (EOF) and -4
		jb pif500
		
pif450:	push bx
		mov ah,62				;
		mov bx,ImpHandle			;close import file
		int 21h
		
pif460:	mov ah,62				;
		mov bx,Handle			;close DXLIB file
		int 21h


pif470:	pop bx				;get error code
		cmp bx,-1				;eof - don't set carry - not an error condition
		je ret
		stc					;otherwise exit with error code in BX and carry set
		ret

pif500:	jmp pif100 				;get another bank
	



;END OF MAIN LOOP_________________________________________________________________________ 





;Routine initialises input buffer DXEXIM_BUF and reads 32 voices
;if input file contains packed DX7 voice data 
;or voices have headers, data is is first placed
;in a temporary buffer HSBUF and unpacked
;and/or stripped before transfer to input buffer

read_voices:  				
	
		push di					;use this as BX needs to be maintained
		push si
		push ax
		push bx
		push cx
		push dx


rv5:		mov di,offset DXEXIM_BUF
		mov cx,5000
		call zeroise				;initialise input buffer


rv20:		cmp dxpacked,0				;unpacked voices without headers 32*155=4960
		jne rv30
		mov cx,4960				
		mov dx,offset DXEXIM_BUF
		jmp rv80

rv30:		cmp dxpacked,1				;unpacked voices with headers
		jne rv40
		mov cx,5216
		jmp rv70

rv40:		cmp dxpacked,2
		jne rv50				
		mov cx,4096				;packed with header 32*128 = 4096
		jmp rv70

rv50:		cmp dxpacked,3				;packed without header
		je rv60					
		mov bx,-3				;error: file type unrecognised
		push bx
		jmp rv200

rv60:		mov cx,4104				;packed with header 32*128 = 4104

rv70:		mov dx,offset HSBUF		;read to scratch buffer for unpacking to DX7READ_BUF
	

rv80:		mov bx,impHandle
		call read_32voices			;routine returns bx=-1 if EOF detected

rv90:		push bx
		cmp bx,0				;full read
		je rv120				;check for partial read
			
rv100:	cmp bx,-2				;error nothing read				
		je rv200				;allow partial read
	

rv120:	cmp dxpacked,1				;check to see if DX voices are packed
		jbe rv140
		call unpack_32DXvoices			;if so unpack them
		jnc rv200				;check for unpacking error
	
rv130:	mov bx,-4
		push bx
		jmp rv200

rv140:	cmp dxpacked,1				;remove headers from unpacked voices with headers
		jb rv200
		call strip_headers	

rv200:	clc
		pop bx
		cmp bx,0				;full read
						;error codes in bx
						;-1 	EOF detected
						;-2 	nothing read
						;-3	unreognised filetype
						;-4	error unpacking voices
		je rv250
		stc

rv250:	pop dx 
		pop cx 
		pop ax 
		pop ax 
		pop si 
		pop di
	
		ret

	
zeroise:

init5:	mov b[di],0
 		inc di
 		loop init5
 		ret
;*************************** read_32voices ***********************
;
;
;		Reads file data to buffer and checks for EOF
;
;		input: BX has file handle and DX has address of read buffer
;			 CX has number of bytes to read
;
;		returns BX=-1 if EOF detected or BX = 0 if full read
;			  BX =-2  if zero read
;
;*****************************************************************

read_32voices:	


r3200:	mov thandle,bx			;store file handle passed in bx to local variable
		MOV AX,03f00h			;read from file						
		INT 21H

r3220:	cmp ax,0				;check for zero read
		jg r3230				;full or partial buffer read

r3225:	mov bx,-2				;shouldn't happen - no bytes read, EOF not detected on previous read
		ret

; check for end of file


r3230:	mov ah,66				;get current file position
		mov al,1
		mov cx,0
		mov dx,0
		int 21h				;dx has msb,ax lsb

		push ax				;store file position so it can be reset
		push dx
		push ax
		push dx

r3235:	mov ah,66				;find size of file
		mov al,2
		mov cx,0
		mov dx,0
		int 21H
		JC r3225				;error finding size
		
		pop cx				;get MSB of filesize
		

r3240:	cmp cx,dx
 		je r3250
		mov bx,0
		pop cx
		jmp r3270

r3250: 	mov bx,0
		pop cx				;get LSB of filesize
		cmp cx,ax
 		jne r3270						

r3260:	mov bx,-1				;end of file this read

r3270:	pop cx				;MSB of file position
		pop dx				;LSB of file position
		push bx
		mov bx,thandle			;restore the file handle to bx
		mov ah,66
		mov al,0
		int 21h				;reset file pointer to current read position

		pop bx				;get return code 0=full read/not eof
							;		    -1=full or partial read/eof
		ret


;Routine to unpack bytes and place resolved bits in output buffer
;There are 32 packed voices in a bank with a 6 byte header
;4096 bytes of voice data a checksum and F7 end of sysex flag making
;4104 bytes in all

Unpack_32DXVoices:     

	
		MOV DXINPUTFILE_OFFSET,6	;set initial offset in input buffer assuming packed file has header
		cmp dxpacked,2			;this is value for no header
		jne unp50 
		MOV DXINPUTFILE_OFFSET,0

unp50:	MOV DI,OFFSET DXEXIM_BUF   	;pointer to beginning of output buffer
		call punk
		ret


;Routine to strip headers and checksum/EOS off type 1 files : unpacked voices with headers
;input data for 64 voices has been read into scratch buffer, stripped voices are put in
;main input buffer

strip_headers:	

		push di
		push si
		push ax
		push bx
		push cx
		push dx

		MOV SI,OFFSET HSBUF
		MOV DI,OFFSET DXEXIM_BUF

		mov cx,32			;split buffer into 32 voices
		
sh10:		push cx
		mov cx,163
	
sh20:		mov al,[si]
		cmp cx,157			;ignore checksum and EOS
		ja sh40
		cmp cx,3			;ignore header
		jb sh40

sh30:		mov [di],al			;transfer data bytes
		inc di

sh40:		inc si
		

sh50:		loop sh20

sh60:		pop cx
		loop sh10

		pop dx 
		pop cx 
		pop ax 
		pop ax 
		pop si 
		pop di
		ret



;*************************** export_file *********************
;
;	exports the current library file dxlib.dat
;	four types of file export are offered: unpacked/packed and with/withput header
;
;
;Unpacked - without header       155 bytes per voice , 4960 bytes per bank of 32 voices
;Unpacked - with header          163 bytes per voice , 5216 bytes per bank of 32 voices
;Packed   - without header       128 bytes per voice , 4096 bytes per bank of 32 voices
;Packed   - with header          128 bytes per voice , 4104 bytes per bank of 32 voices (128*32)+ 8
;
;
;	input: none
;	output: al,0 if all OK otherwise -1 for error/abort
;
;**************************************************************

export_file: 	

			scrsav CBRKBUF,24,1		;store the bottom line of library screen

ef50:			call get_filename			;get the export file name
			cmp al,0
			jne ef400

ef100:		call create_efile			;create the export file
			jnc ef300
			cmp ax,-1				;Esc pressed to abort
			je ef400

			cmp ax,-2				;getting another filename
			je ef50

ef150:		BLANK
          		MESSAGE 3842,31,fileopenerr,66,24,68;ask user whether to recyle or abort
			
ef200:		mov ah,12				;reset keyboard buffer then get the character	
			mov al,1
			int 21h

ef250:		cmp al,27				;Esc pressed to abort
			jne ef50
			mov al,-1				;return code
			jmp ef400
		
			
ef300:		call getexport_type		;determine export format
			cmp ax,-1				;aborting
			je ef400
			 
			call process_efile		;process export file
			mov al,0				;return code - all OK
			jnc ef400				;transfer to dxlib.dat

ef350:		mov al,-1				;return error code, check BX for detail

ef400:		push ax
			scres CBRKBUF,24,1		;restore the bottom line of the screen
			pop ax
			ret



;************************ getexport_type ***************
;
;	get's user input on type of export file required
;	there are four options : packed/unpacked and header/no header
;
;	input:none
;	output: Al,0 if function completed OK, otherwise -1
;		  if aborting
;
;		  the variable dxpacked is set to the chosen format
;		  	0  	unpacked without header
;			1	unpacked with header
;			2	packed without header
;			3	packed with header
;
;
;************************************************************

getexport_type:	BLANK
          		MESSAGE 3842,31,efilemsga,67,24,69;ask whether to pack export data
		
gete10:		mov ah,1			;get the character	
			int 21h
			cmp al,27			;Esc pressed to abort
			jne gete20

gete15:		mov al,-1			;return code
			ret

gete20:		push ax
			BLANK
          		MESSAGE 3842,31,efilemsgb,72,24,73;ask whether header required

gete30:		mov ah,1			;get the character	
			int 21h
			cmp al,27			;Esc pressed to abort
			je getexport_type

gete50:		pop bx
			cmp bl,"P"			;see if we are packing data
			je gete60
			cmp bl,"p"
			jne gete100

gete60:		mov dxpacked,2		;packed without
			cmp al,"H"
			je gete70
			cmp al,"h"
			jne gete130			;exit OK

gete70:		mov dxpacked,3		;packed with header
			jmp gete130

gete100:		mov dxpacked,0		;unpacked without
			cmp al,"H"
			je gete110
			cmp al,"h"
			jne gete130			;exit OK

gete110:		mov dxpacked,1
	
gete130:		mov ax,0
			ret




;******************** create_efile *************************
;
;
;	tries to create an export file with the name entered
;	in previous call to get_filename. If file already exists
;	or any element of the pathname is incorrect an error
;	is returned. User can override error in case of previously
;	exisiting file,and file is opened for read/write access
;
;	input:target filename in buffer nam 
;	output: carry flag clear if all OK otherwise carry flag set with
;			error code in AX :
;				-1			Esc pressed abort
;				-2			get new filename
;				3			error in pathname
;				4			no handles available
;				5			access denied
;
;**********************************************************


create_efile:

		push bx
		push cx
		push dx
	
		MOV AH,60				;create new file
		MOV CX,0				;read/write access
		MOV DX,OFFSET nam			;file name
		inc dx,2
		INT 21H

 		JC cef50				;file create error

cef20:	MOV ExpHandle,AX			;store file handle
		mov ax,0
		jmp cef220

cef50:	cmp ax,050h				;file already exists
		jne cef220				;return with error code		
							;error in pathname,   AX=3
							;no handles available AX=4
							;access denied        AX=5
cef100:	BLANK
          	MESSAGE 3842,31,fileunavail,55,24,57;ask user whether to replace or abort
		
cef150:	mov ah,12				;reset keyboard buffer then get the character	
		mov al,1
		int 21h

cef200:	cmp al,27				;Esc pressed to abort
		jne cef250
		mov ax,-1				;return code

cef220:	jmp cef400
		
cef250:	cmp al,"R"
		je cef300
		cmp al,"r"
		je cef300
		beep					;invalid entry recycle
		jmp cef100

cef300:	MOV AH,60				;truncate existing file
		MOV CX,0				;read/write access
		MOV DX,OFFSET nam			;file name
		inc dx,2				;jump over first 2 bytes: buffer and file lengths
		INT 21H

 		JC cef400				;file create error

cef350:	MOV ExpHandle,AX			;store file handle
		mov ax,0



cef400:	pop dx
		pop cx
		pop bx
		ret



;********************* process_efile **************************
;
;		exports dxlib.dat to file in one of four formats
;		packed/unpacked voices, with/without headers
;
;		input:none
;		output: BX=0 and carry clear if all read/transferred OK
;			otherwise carry set and BX carries error code
;
					;error codes in bx
					;-1 	EOF detected
					;-2 	nothing read
					;-3	unrecognised filetype
					;-4	error unpacking voices
;
;*********************************************************************




process_efile:	mov ax,0			;initialise bank counter
			mov ax,04200h		;set file pointer to beginning of dxlib.dat
			mov cx,0			
			mov dx,0
			mov bx,Handle
			int 21h		
							;banks loop here
pef100:	inc ax,1				;increment bank counter
		push ax				;stack bank counter
		mov di,3980
		call scrpt_ascii			;display bank number

pef200:	call write_voices			;initialise bank buffer,read 32 voices
							;from dxlib,pack/add headers and write 
							;them to export file
							;return code in BX
		pop ax				;get bank counter
		jnc pef100				;no error and not eof so get another bank

pef400:	clc
		push bx
		mov ah,62				;
		mov bx,expHandle			;close export file
		int 21h

pef450:	pop bx				;get error code
		cmp bx,-1				;eof - don't set carry - not an error condition
		je ret
		stc					;otherwise exit with error code in BX and carry set
		ret

;END OF MAIN LOOP_________________________________________________________________________ 



;*************************** scrpt_ascii **********************
;
;
;		Prints binary no in AX to screen as ascii
;
;		input: AX, no to be printed
;			 DI, screen location
;
;		output:none
;
;**************************************************************


scrpt_ascii:mov dx,0				;initialise MSB
	    
sca20:	DIV THOUSAND
		push dx				;store remainder
	
sca30:	mov dx,0				;flag set to 0 - don't print remainder
		call bnkno_scr
		pop ax				;get thousands' remainder
		
		DIV HUNDRED				;ah now has remainder (tens),AL hundreds
		mov dx,0				;flag set to 0 - don't print remainder
		
sca40:	call bnkno_scr			;print hunder's value
		shr ax,8				;put ah, into al
		DIV TEN				;al now has tens, ah units
		
sca50:	mov dx,1				;print remainder
		call bnkno_scr

		
sca70:	ret



;********************* write_voices **************************
;
;		initialises export buffer HSBUF,gets
;		32 voices from DXLIB and writes them to export file
;		If export file is to contain packed DX7 voice data 
;		or is unpacked but has to have headers added, the
;		data is is first placed in a temporary buffer DXEXIM_BUF 
;		and packed,and/or header added, before transfer to 
;		the output buffer HSBUF
;
;		input:none
;		output: BX=0 and carry clear if all read/transferred OK
;			otherwise carry set and BX carries error code
;
					;error codes in bx
					;-1 	EOF detected
					;-2 	error reading from DXLIB - nothing read
					;-3	unrecognised export filetype
					;-4	error packing voices
					;-5   error writing to export file
;
;*********************************************************************



write_voices:  				
	
		push di					;use this as BX needs to be maintained
		push si
		push ax
		push bx
		push cx
		push dx


wv50:		mov bx,handle				;get 32 voices
		mov dx,offset dxexim_buf
		mov cx,4960
		cmp dxpacked,0				;type 0 can go straight to export file
		jne wv100
		mov dx,offset hsbuf
		
		

wv100:	call read_32voices
	
		cmp bx,-2					;nothing read so abandon
		je wv720
		push bx 
								;stack r eturn code
								;0=full read/not eof
								;-1 = full or partial read/eof
								;-2 = error, nothing read
		
wv150:	cmp dxpacked,0				;unpacked voices without headers 32*155=4960
		jne wv200
		mov cx,4960				
		jmp wv600					;put straight into export file

		
wv200:	cmp dxpacked,1				;unpacked but add header
		je wv450 

		mov si,offset dxexim_buf
		
		mov di,offset HSBUF
		mov cx,5000
		call zeroise				;initialise output buffer

wv250:	cmp dxpacked,3				;check to see if DX voices are to be packed
		je wv300					;types 2&3
		cmp dxpacked,2
		je wv300	
								
		pop bx					;can't continue so get rid of
								;read return code (0 or -1)
		mov bx,-3					;error: file type unrecognised
		push bx
		jmp wv720


wv300:	call pack					;if so pack them
		jnc wv400					;check for packing error
	
wv350:	pop bx					;get rid of read return code
		mov bx,-4					
		push bx
		jmp wv720					;set carry and exit

wv400:	cmp dxpacked,3
		jne wv600					

wv450:	call add_headers

		
wv600:	call write_32voices			;routine transfers bank from HSBUF to export
								;file. returns bx=-5 if write error occurs
							
		jnc wv700
		pop ax					;get rid of read return code
		jmp wv720
		
	
wv700:	pop bx			;will be either 0 (full read/not eof) or 
						; -1 (full or partial read/eof)
		cmp bx,0
		je wv750

wv720:	stc				;set carry for EOF

wv750:	pop dx 
		pop cx 
		pop ax 
		pop ax 
		pop si 
		pop di
	
		ret

;****************************** write_32voices ***************
;
;	Write 32 voices (packed/unpacked, with/without header)
;	 to export file
;
;	input: none
;	output:carry flag is set if write fails 
;	
;
;*****************************************************************


write_32voices:

		cmp dxpacked,0
		jne w3210
		mov cx, 4960
		jmp w3240

w3210:	cmp dxpacked,1
		jne w3220
		mov cx,5216
		jmp w3240

w3220:	cmp dxpacked,2
		jne w3230
		mov cx,4096
		jmp w3240

w3230:	mov cx,4104				;must be packed with header,type 3

w3240:	mov dx,offset HSBUF		;write bank to export file
		mov ax,04000h			;write to file function code
		mov bx,expHandle			;export file handle
		int 21h
		
w3250:	mov bx,0
		jnc ret



w3260:	mov bx,-5				;either write error or invalid voice type
		ret

;**************************** add_headers ************************
;
;		Adds headers and checksum/EOS to type 1 and 3 files : 
;		unpacked or packed voices with headers. Type 3 adds
;		header/eos to whole bank, type 1 to each voice
;		
;
;		input:for type 1 voice data is in buffer dxexim_buf
;			for type 3 it's in HSBUF
;		output:none
;
;*****************************************************************

add_headers:	

		push di
		push si
		push ax
		push bx
		push cx
		push dx

		;type 3 packed with header 
		
ah100:	cmp dxpacked,3
		jne ah150
		call t3_header
		jmp ah500

		;type 1 unpacked with header

ah150:	MOV DI,OFFSET HSBUF
		MOV SI,OFFSET DXEXIM_BUF

		mov cx,32			;split buffer into 32 voices
		

		;put intro header in HSBUF

ah200:	push cx
		mov al,0f0h			;240 start of sysex
		mov [di],al
		inc di

		mov al,67			;67	
		mov [di],al
		inc di

		mov ax,0			;channel no and format number - both 0 ch1 and 1 voice
		mov [di],ax
		inc di,2
			
		mov ax,0			;byte count 2 bytes
		mov [di],ax
		inc di,2		

		;do checksum

ah250:      MOV AX,0
          	MOV DX,0
          	MOV CX,155

ah300:      MOV AL,B[SI]              	
		mov [di],al				;put byte in output buffer
          	CBW
          	ADD DX,AX				;accumulate sum of bytes
          	INC SI
		inc di
          	LOOP ah300

ah350:      MOV AX,DX                   ;sum of bytes
          	NOT AX                      ;twos' complement
          	ADD AX,1
          	SHL AL,1                    ;zeroise MSB
          	SHR AL,1			    ;checksum is in AL

ah400:	mov [di],al				;put checksum in output buffer
		inc di
		mov al,0f7				;end of sysex
		mov [di],al
		inc di

ah450:	pop cx
		loop ah200

ah500:	pop dx 
		pop cx 
		pop ax 
		pop ax 
		pop si 
		pop di
		ret


;******************* t3_header *********************************
;
;	Puts header and checksum/eos on packed bank
;
;
;	input: packed bank is in HSBUF 4096 bytes
;	output: HSBUF with 4104 bytes
;
;***************************************************************


	;shift everything 6 bytes to the right
	;and calculate sum of bytes

t3_header:	mov si,offset HSBUF
		mov di,offset HSBUF
		mov cx,4095
		add si,cx				;puts us at last byte of bank
		add di,cx
		add di,6
		mov dx,0

t3h150:	mov al,[si]
		mov [di],al
		cbw
		add dx,ax
		dec si
		dec di
		loop t3h150

	;calculate checksum

t3h200:     
		MOV AX,DX                   	;sum of bytes
          	NOT AX                      	;twos' complement
          	ADD AX,1
          	SHL AL,1                    	;zeroise MSB
          	SHR AL,1			    	;checksum is in AL

t3h300:	mov di,offset hsbuf
		add di,4102
		mov [di],al				;put checksum in output buffer
		inc di
		mov al,0f7				;end of sysex
		mov [di],al

	;put header in buffer


t3h350:	mov di,offset hsbuf		;back to beginning

		mov al,0f0h				;240 start of sysex
		mov [di],al
		inc di

		mov al,67				;67	
		mov [di],al
		inc di

		mov ax,9				;channel no 0 and format number 9
		mov [di],ax
		inc di,2
			
		mov ah,020h				;byte count 2 bytes
		mov al,0
		mov [di],ax		

		mov di,offset hsbuf
		ret





     ;    T5.TXT    TSR ROUTINES


     ;************  Interrupt Handlers  ********************************

INT28:	
                PUSHF               ;chain to prior installed 28 handler
                CS CALL INT28_VEC

	        STI

		PUSH BP
		MOV BP,SP
	        PUSH AX
	        PUSH DS
		PUSH ES

		MOV AX,CS
		MOV DS,AX

                CLI
                CS CMP BUSY,0       ;test 'busy' flag to prevent recursive
                                        ;entry
                STI
                JE B01

B04:    	POP ES
		POP DS
		MOV AL,32
	        OUT 32,AL
		POP AX
                POP BP
		IRET

B01:           CMP IN_BIOS,0       ;Test for BIOS disk activity
               JNE B04
                    
B02:           MOV AX,0	       	   ;Test if hotkey depressed
               MOV ES,AX           ;Read BIOS keyboard flag at
                                       ;0417H
               ES MOV AX,WORD PTR [417H]
	       AND AX,10
               CS CMP AX,HOTKEY           ;see if its the 'hot key'
	       JNE B04


B03:           pop es
	       pop ds
	       push ds
	       push es 
		CALL ACTIVATE       ;run DXPRO
	       JMP B04		


INT13:    PUSHF               ;chain to original interrupt
	  CS MOV IN_BIOS,1
	  CS CALL INT13_VEC
	  CS MOV IN_BIOS,0
	  RETF 2		


INT09:    
	  PUSHF               ;chain to original interrupt
       	  CS CALL INT09_VEC
	  STI
	  PUSH BP	  
	  MOV BP,SP
	  PUSH AX	
	  PUSH DS
	  PUSH ES

	  MOV AX,CS
	  MOV DS,AX

	  CLI

          CS CMP BUSY,0       ;test busy flags to avoid recursion

          JE C01

C06:      POP ES
	  POP DS

    	  MOV AL,32
	  OUT 32,AL

	  POP AX
	  POP BP
          IRET



C01:	CS CMP IN_BIOS,0    ;test for BIOS disk activity
          JNE C06

C02:	  MOV AX,0
          MOV ES,AX           ;Read BIOS keyboard flag at
                                        ;0417H
          ES MOV AX,WORD PTR [417H]
	  AND AX,10		;only read bits 1& 3
          CS CMP AX,HOTKEY    ;test  flag 
	  JNE C06

C03:  
	  PUSH DS              ;not busy and hot key pressed
          PUSH BX
          CS LDS BX,INDOS_PTR  ;load pointer to 'INDOS' flag
          DS CMP B[BX],0        ;test INDOS flag

          POP BX
          POP DS
	  JNE C06
C04:    pop es
        pop ds 			;restore originating programs data seg for saving 
	push ds
	push es
	CALL ACTIVATE       ;not busy,not in DOS and key depressed
	  JMP C06





ACTIVATE:	   MOV CS:BUSY,1       ;set flag to prevent recursive calls
                   MOV CS:DOS_SP,SP
		   MOV CS:DOS_SS,SS
		   MOV CS:_SDA_DSEG,DS	;save originating program's data segment
		  
                   MOV SS,CS:C_SS      ;set up DXPRO stack pointer
                   MOV SP,CS:C_SP      ;set up DXPRO stack pointer

                    PUSH AX
                    PUSH BX
                    PUSH CX
                    PUSH DX
                    PUSH BP
                    PUSH SI
                    PUSH DI
                    PUSH DS
                    PUSH ES


                    MOV CX,128           ;save the DOS stack
                    MOV ES,CS:DOS_SS    ;point to top of DOS stack
                    MOV SI,CS:DOS_SP

D01:                PUSH WORD PTR ES:[SI]         ;save all 64 words of stack
                    INC SI,2
                    LOOP D01


                    MOV AH,2FH          ;save DTA for interrupted program
                    INT 21H
                    MOV CS:D_DTA_OFF,BX
                    MOV CS:D_DTA_SEG,ES

                    MOV AH,1AH          ;set up DXPRO DTA
                   MOV DX,CS:C_DTA_OFF
                   MOV DS,CS:C_DTA_SEG
                   INT 21H
		   
		    MOV AH,51H		;save PSP for interrupted program
		    INT 21H
		    CS MOV D_PSP,BX
		
		    MOV AH,50H		;set up DXPRO PSP
		    CS MOV BX,C_PSP
		    INT 21H
		



D03:                MOV DS,CS:C_DS      ;set up DXPRO segment registers
                    MOV ES,CS:C_ES      ;set up DXPRO segment registers

	   	    MOV AH,25			;get originating program's drive
	            INT 21H
	            MOV DL,AL
	            ADD DL,65			;0=A etc
	            CS MOV SI,OFFSET DIRECTPM
	            MOV [SI],DL
		MOV AL,":"
	            MOV [SI+1],AL
		MOV AL,"\"
	            MOV [SI+2],AL

		
		    MOV AH,71		;get originating program's path
		    CS MOV SI,OFFSET DIRECTPM
		    INC SI,3
		    DEC DL,64
		    INT 21H


		    MOV AH,59		;set DXPRO's directory
		    CS MOV DX,OFFSET DIRECTDX
		    INT 21H
		    JNC DXST

D04:		    MOV AH,76
		    INT 21H


;DXPRO now activated

DXST:		    STI
                    SCRSAV TSRBUF,25,0	   ;save screen
		    CS CALL DXPRO          ;run DXPRO

D02:                CLI

		    MOV AH,59			;set originating program's directory
    		    CS MOV DX,OFFSET DIRECTPM
		    INT 21H


    		    MOV AH,50H		;restore originating program PSP
		    CS MOV BX,D_PSP
		    INT 21H

                    MOV AH,1AH          ;set up interrupted  DTA
                    MOV DX,CS:D_DTA_OFF
                    MOV DS,CS:D_DTA_SEG
                    INT 21H

                    MOV CX,128           ;restore the DOS stack
                    MOV ES,CS:DOS_SS    ;point to top of DOS stack
                    MOV SI,CS:DOS_SP
                    ADD SI,256

D02:                DEC SI,2            ;loop to restore 64 words of DOS stack
                    POP WORD PTR ES:[SI]
                    LOOP D02

                    POP ES              ;restore machine state
                    POP DS
                    POP DI
                    POP SI
                    POP BP
                    POP DX
                    POP CX
                    POP BX
                    POP AX

                    MOV SS,CS:DOS_SS    ;restore stack of interrupted prog
                    MOV SP,CS:DOS_SP
		    MOV DS,CS:_SDA_DSEG	;restore originating program's data segment

                    MOV CS:BUSY,0       ;reset busy flag

	            STI
                    RET
TENDP:

     ;******************* Installation Procedure ****************************

TSR:	  MOV AH,25			;get DXPRO's drive
	  INT 21H
	  MOV DL,AL
	  ADD DL,65			;0=A etc
	  CS MOV SI,OFFSET DIRECTDX
	  MOV [SI],DL
	MOV AL,":"
	  MOV [SI+1],AL
	MOV AL,"\"  
	MOV [SI+2],AL

	  MOV AH,71			;get DXPRO's directory
	  SUB DL,64
	  CS MOV SI,OFFSET DIRECTDX	 
	  ADD SI,3
	  INT 21H
	  
	  PUSH DS
	  PUSH ES
	  PUSH BX
	  PUSH DI
 
          CS MOV C_SS,SS      ;save DXPRO's SS
          CS MOV C_SP,SP      ;save DXPRO's SP
          CS MOV C_DS,DS      ;save DXPRO's DS
          CS MOV C_ES,ES      ;save DXPRO's ES

	  SCRES TSRBUF,25,0
	  
          MOV AX,OFFSET START_HERE          ;address of DXPRO start
          CS MOV DXPRO,AX

	  XOR AX,AX
          MOV AL,10                      ;defines hotkey as Alt-left shift
          CS MOV HOTKEY,AX

          MOV AH,2FH                    ;get DXPRO disk transfer address
          INT 21H                       
          CS MOV C_DTA_OFF,BX           ;save it
          CS MOV C_DTA_SEG,ES           ;save it

	  MOV AH,51H			;get DXPR0 PSP segment
	  INT 21H
	  CS MOV C_PSP,BX		;save it

	  MOV AH,35H			;get cntrl brk vector
	  MOV AL,1BH			
	  INT 21H
	  CS MOV DOSEG,ES		;save it
	  CS MOV DOSOFF,BX

          MOV AH,34H                    ;call Dos to retrieve pointer to INDOS
          MOV AL,0                      ;flag
          INT 21H
          CS MOV DI,OFFSET INDOS_PTR
          CS MOV [DI],BX           ;save old vector
          CS MOV [DI+2],ES         

          MOV AH,53                    ;get old interrupt 28H vector
          MOV AL,28H
          INT 33
	  CS MOV DI,OFFSET INT28_VEC
          CS MOV [DI],BX             ;save it
          CS MOV [DI+2],ES           ;save it


TSR4:     MOV AH,53                    ;get old interrupt 09H vector
          MOV AL,13H
          INT 33
          CS MOV DI,OFFSET INT13_VEC
          CS MOV [DI],BX
          CS MOV [DI+2],ES



TSR5:     MOV AH,53                    ;get old interrupt 09H vector
          MOV AL,09H
          INT 33
          CS MOV DI,OFFSET INT09_VEC
          CS MOV [DI],BX
          CS MOV [DI+2],ES

           PUSH DS                     ;Set new interrupt 28h vector
           MOV AH,37
           MOV AL,28H
           CS MOV DX,OFFSET INT28
	   PUSH CS
	   POP DS
           INT 33
	   POP DS

KEY0:     PUSH DS                     ;Set new interrupt 13h vector
          MOV AH,37
          MOV AL,13H
          MOV DX,OFFSET INT13
	  PUSH CS
	  POP DS
          INT 33
	  POP DS


KEY1:     PUSH DS                     ;Set new interrupt 09h vector
          MOV AH,37
          MOV AL,09H
          CS MOV DX,OFFSET INT09
	  PUSH CS
	  POP DS
          INT 33
	  POP DS
	  CS MOV TSRFLG,99		;set flag to prevent reinstallation

     ;Now call terminate and stay resident service

TSR2:	  POP DI
	  POP BX
	  POP ES
	  POP DS

	  MOV AH,9
	  MOV DX,OFFSET TSRINST
	  INT 21H

	  MOV DX,OFFSET TENDP
	  ADD DX,15
	  MOV AX,DX
	  MOV DX,0
	  DIV SIXTEEN
	  ADD AX,0800H
	  MOV DX,AX
	  MOV AH,49
	  MOV AL,1
          INT 21H
	  RET




